mirror of
https://github.com/krahets/hello-algo.git
synced 2026-06-16 07:08:21 +08:00
Add ru version (#1865)
* Add Russian docs site baseline * Add Russian localized codebase * Polish Russian code wording * Update ru code translation. * Update code translation and chapter covers. * Fix pythontutor extraction. * Add README and landing page. * placeholder of profiles * Use figures of English version * Remove chapter paperbook
This commit is contained in:
1
ru/codes/python/.gitignore
vendored
Normal file
1
ru/codes/python/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
__pycache__
|
||||
100
ru/codes/python/chapter_array_and_linkedlist/array.py
Normal file
100
ru/codes/python/chapter_array_and_linkedlist/array.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
File: array.py
|
||||
Created Time: 2022-11-25
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
|
||||
def random_access(nums: list[int]) -> int:
|
||||
"""Случайный доступ к элементу"""
|
||||
# Случайным образом выбрать число из интервала [0, len(nums)-1]
|
||||
random_index = random.randint(0, len(nums) - 1)
|
||||
# Получить и вернуть случайный элемент
|
||||
random_num = nums[random_index]
|
||||
return random_num
|
||||
|
||||
|
||||
# Обратите внимание: list в Python — это динамический массив, его можно расширять напрямую
|
||||
# Для удобства обучения в этой функции list рассматривается как массив неизменяемой длины
|
||||
def extend(nums: list[int], enlarge: int) -> list[int]:
|
||||
"""Увеличить длину массива"""
|
||||
# Инициализировать массив увеличенной длины
|
||||
res = [0] * (len(nums) + enlarge)
|
||||
# Скопировать все элементы исходного массива в новый массив
|
||||
for i in range(len(nums)):
|
||||
res[i] = nums[i]
|
||||
# Вернуть новый массив после расширения
|
||||
return res
|
||||
|
||||
|
||||
def insert(nums: list[int], num: int, index: int):
|
||||
"""Вставить элемент num по индексу index в массив"""
|
||||
# Сдвинуть элемент с индексом index и все последующие элементы на одну позицию назад
|
||||
for i in range(len(nums) - 1, index, -1):
|
||||
nums[i] = nums[i - 1]
|
||||
# Присвоить num элементу по индексу index
|
||||
nums[index] = num
|
||||
|
||||
|
||||
def remove(nums: list[int], index: int):
|
||||
"""Удалить элемент по индексу index"""
|
||||
# Сдвинуть все элементы после индекса index на одну позицию вперед
|
||||
for i in range(index, len(nums) - 1):
|
||||
nums[i] = nums[i + 1]
|
||||
|
||||
|
||||
def traverse(nums: list[int]):
|
||||
"""Обход массива"""
|
||||
count = 0
|
||||
# Обход массива по индексам
|
||||
for i in range(len(nums)):
|
||||
count += nums[i]
|
||||
# Непосредственно обходить элементы массива
|
||||
for num in nums:
|
||||
count += num
|
||||
# Одновременно обходить индексы и элементы данных
|
||||
for i, num in enumerate(nums):
|
||||
count += nums[i]
|
||||
count += num
|
||||
|
||||
|
||||
def find(nums: list[int], target: int) -> int:
|
||||
"""Найти заданный элемент в массиве"""
|
||||
for i in range(len(nums)):
|
||||
if nums[i] == target:
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация массива
|
||||
arr = [0] * 5
|
||||
print("Массив arr =", arr)
|
||||
nums = [1, 3, 2, 5, 4]
|
||||
print("Массив nums =", nums)
|
||||
|
||||
# Случайный доступ
|
||||
random_num: int = random_access(nums)
|
||||
print("Случайный элемент из nums =", random_num)
|
||||
|
||||
# Расширение длины
|
||||
nums: list[int] = extend(nums, 3)
|
||||
print("После увеличения длины массива до 8 nums =", nums)
|
||||
|
||||
# Вставка элемента
|
||||
insert(nums, 6, 3)
|
||||
print("После вставки числа 6 по индексу 3 nums =", nums)
|
||||
|
||||
# Удаление элемента
|
||||
remove(nums, 2)
|
||||
print("После удаления элемента по индексу 2 nums =", nums)
|
||||
|
||||
# Обход массива
|
||||
traverse(nums)
|
||||
|
||||
# Поиск элемента
|
||||
index: int = find(nums, 3)
|
||||
print("Поиск элемента 3 в nums: индекс =", index)
|
||||
85
ru/codes/python/chapter_array_and_linkedlist/linked_list.py
Normal file
85
ru/codes/python/chapter_array_and_linkedlist/linked_list.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
File: linked_list.py
|
||||
Created Time: 2022-11-25
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import ListNode, print_linked_list
|
||||
|
||||
|
||||
def insert(n0: ListNode, P: ListNode):
|
||||
"""Вставить узел P после узла n0 в связном списке"""
|
||||
n1 = n0.next
|
||||
P.next = n1
|
||||
n0.next = P
|
||||
|
||||
|
||||
def remove(n0: ListNode):
|
||||
"""Удалить первый узел после узла n0 в связном списке"""
|
||||
if not n0.next:
|
||||
return
|
||||
# n0 -> P -> n1
|
||||
P = n0.next
|
||||
n1 = P.next
|
||||
n0.next = n1
|
||||
|
||||
|
||||
def access(head: ListNode, index: int) -> ListNode | None:
|
||||
"""Доступ к узлу связного списка по индексу index"""
|
||||
for _ in range(index):
|
||||
if not head:
|
||||
return None
|
||||
head = head.next
|
||||
return head
|
||||
|
||||
|
||||
def find(head: ListNode, target: int) -> int:
|
||||
"""Найти в связном списке первый узел со значением target"""
|
||||
index = 0
|
||||
while head:
|
||||
if head.val == target:
|
||||
return index
|
||||
head = head.next
|
||||
index += 1
|
||||
return -1
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация связного списка
|
||||
# Инициализация всех узлов
|
||||
n0 = ListNode(1)
|
||||
n1 = ListNode(3)
|
||||
n2 = ListNode(2)
|
||||
n3 = ListNode(5)
|
||||
n4 = ListNode(4)
|
||||
# Построить ссылки между узлами
|
||||
n0.next = n1
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = n4
|
||||
print("Исходный связный список")
|
||||
print_linked_list(n0)
|
||||
|
||||
# Вставка узла
|
||||
p = ListNode(0)
|
||||
insert(n0, p)
|
||||
print("Связный список после вставки узла")
|
||||
print_linked_list(n0)
|
||||
|
||||
# Удаление узла
|
||||
remove(n0)
|
||||
print("Связный список после удаления узла")
|
||||
print_linked_list(n0)
|
||||
|
||||
# Доступ к узлу
|
||||
node: ListNode = access(n0, 3)
|
||||
print("Значение узла по индексу 3 в связном списке = {}".format(node.val))
|
||||
|
||||
# Поиск узла
|
||||
index: int = find(n0, 2)
|
||||
print("Индекс узла со значением 2 в связном списке = {}".format(index))
|
||||
56
ru/codes/python/chapter_array_and_linkedlist/list.py
Normal file
56
ru/codes/python/chapter_array_and_linkedlist/list.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
File: list.py
|
||||
Created Time: 2022-11-25
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация списка
|
||||
nums: list[int] = [1, 3, 2, 5, 4]
|
||||
print("\nСписок nums =", nums)
|
||||
|
||||
# Доступ к элементу
|
||||
x: int = nums[1]
|
||||
print("\nЭлемент по индексу 1: x =", x)
|
||||
|
||||
# Обновление элемента
|
||||
nums[1] = 0
|
||||
print("\nПосле обновления элемента по индексу 1 до 0 nums =", nums)
|
||||
|
||||
# Очистить список
|
||||
nums.clear()
|
||||
print("\nПосле очистки списка nums =", nums)
|
||||
|
||||
# Добавление элемента в конец
|
||||
nums.append(1)
|
||||
nums.append(3)
|
||||
nums.append(2)
|
||||
nums.append(5)
|
||||
nums.append(4)
|
||||
print("\nПосле добавления элементов nums =", nums)
|
||||
|
||||
# Вставка элемента в середину
|
||||
nums.insert(3, 6)
|
||||
print("\nПосле вставки числа 6 по индексу 3 nums =", nums)
|
||||
|
||||
# Удаление элемента
|
||||
nums.pop(3)
|
||||
print("\nПосле удаления элемента по индексу 3 nums =", nums)
|
||||
|
||||
# Обходить список по индексам
|
||||
count = 0
|
||||
for i in range(len(nums)):
|
||||
count += nums[i]
|
||||
# Непосредственно обходить элементы списка
|
||||
for num in nums:
|
||||
count += num
|
||||
|
||||
# Объединить два списка
|
||||
nums1 = [6, 8, 7, 10, 9]
|
||||
nums += nums1
|
||||
print("\nПосле конкатенации списка nums1 к nums nums =", nums)
|
||||
|
||||
# Отсортировать список
|
||||
nums.sort()
|
||||
print("\nПосле сортировки списка nums =", nums)
|
||||
118
ru/codes/python/chapter_array_and_linkedlist/my_list.py
Normal file
118
ru/codes/python/chapter_array_and_linkedlist/my_list.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
File: my_list.py
|
||||
Created Time: 2022-11-25
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
class MyList:
|
||||
"""Класс списка"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
self._capacity: int = 10 # Вместимость списка
|
||||
self._arr: list[int] = [0] * self._capacity # Массив (для хранения элементов списка)
|
||||
self._size: int = 0 # Длина списка (текущее число элементов)
|
||||
self._extend_ratio: int = 2 # Коэффициент увеличения списка при каждом расширении
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получить длину списка (текущее число элементов)"""
|
||||
return self._size
|
||||
|
||||
def capacity(self) -> int:
|
||||
"""Получить вместимость списка"""
|
||||
return self._capacity
|
||||
|
||||
def get(self, index: int) -> int:
|
||||
"""Доступ к элементу"""
|
||||
# Если индекс выходит за границы, выбрасывается исключение; далее аналогично
|
||||
if index < 0 or index >= self._size:
|
||||
raise IndexError("индекс выходит за границы")
|
||||
return self._arr[index]
|
||||
|
||||
def set(self, num: int, index: int):
|
||||
"""Обновление элемента"""
|
||||
if index < 0 or index >= self._size:
|
||||
raise IndexError("индекс выходит за границы")
|
||||
self._arr[index] = num
|
||||
|
||||
def add(self, num: int):
|
||||
"""Добавление элемента в конец"""
|
||||
# При превышении вместимости по числу элементов запускается расширение
|
||||
if self.size() == self.capacity():
|
||||
self.extend_capacity()
|
||||
self._arr[self._size] = num
|
||||
self._size += 1
|
||||
|
||||
def insert(self, num: int, index: int):
|
||||
"""Вставка элемента в середину"""
|
||||
if index < 0 or index >= self._size:
|
||||
raise IndexError("индекс выходит за границы")
|
||||
# При превышении вместимости по числу элементов запускается расширение
|
||||
if self._size == self.capacity():
|
||||
self.extend_capacity()
|
||||
# Сдвинуть элемент с индексом index и все следующие элементы на одну позицию назад
|
||||
for j in range(self._size - 1, index - 1, -1):
|
||||
self._arr[j + 1] = self._arr[j]
|
||||
self._arr[index] = num
|
||||
# Обновить число элементов
|
||||
self._size += 1
|
||||
|
||||
def remove(self, index: int) -> int:
|
||||
"""Удаление элемента"""
|
||||
if index < 0 or index >= self._size:
|
||||
raise IndexError("индекс выходит за границы")
|
||||
num = self._arr[index]
|
||||
# Сдвинуть все элементы после индекса index на одну позицию вперед
|
||||
for j in range(index, self._size - 1):
|
||||
self._arr[j] = self._arr[j + 1]
|
||||
# Обновить число элементов
|
||||
self._size -= 1
|
||||
# Вернуть удаленный элемент
|
||||
return num
|
||||
|
||||
def extend_capacity(self):
|
||||
"""Расширение списка"""
|
||||
# Создать новый массив длиной в _extend_ratio раз больше исходного массива и скопировать в него исходный массив
|
||||
self._arr = self._arr + [0] * self.capacity() * (self._extend_ratio - 1)
|
||||
# Обновить вместимость списка
|
||||
self._capacity = len(self._arr)
|
||||
|
||||
def to_array(self) -> list[int]:
|
||||
"""Вернуть список фактической длины"""
|
||||
return self._arr[: self._size]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация списка
|
||||
nums = MyList()
|
||||
# Добавление элемента в конец
|
||||
nums.add(1)
|
||||
nums.add(3)
|
||||
nums.add(2)
|
||||
nums.add(5)
|
||||
nums.add(4)
|
||||
print(f"Список nums = {nums.to_array()}, вместимость = {nums.capacity()}, длина = {nums.size()}")
|
||||
|
||||
# Вставка элемента в середину
|
||||
nums.insert(6, index=3)
|
||||
print("После вставки числа 6 по индексу 3 nums =", nums.to_array())
|
||||
|
||||
# Удаление элемента
|
||||
nums.remove(3)
|
||||
print("После удаления элемента по индексу 3 nums =", nums.to_array())
|
||||
|
||||
# Доступ к элементу
|
||||
num = nums.get(1)
|
||||
print("Элемент по индексу 1: num =", num)
|
||||
|
||||
# Обновление элемента
|
||||
nums.set(0, 1)
|
||||
print("После обновления элемента по индексу 1 до 0 nums =", nums.to_array())
|
||||
|
||||
# Проверка механизма расширения
|
||||
for i in range(10):
|
||||
# При i = 5 длина списка превысит его вместимость, и в этот момент сработает механизм расширения
|
||||
nums.add(i)
|
||||
print(f"Список nums после увеличения вместимости = {nums.to_array()}, вместимость = {nums.capacity()}, длина = {nums.size()}")
|
||||
62
ru/codes/python/chapter_backtracking/n_queens.py
Normal file
62
ru/codes/python/chapter_backtracking/n_queens.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
File: n_queens.py
|
||||
Created Time: 2023-04-26
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def backtrack(
|
||||
row: int,
|
||||
n: int,
|
||||
state: list[list[str]],
|
||||
res: list[list[list[str]]],
|
||||
cols: list[bool],
|
||||
diags1: list[bool],
|
||||
diags2: list[bool],
|
||||
):
|
||||
"""Алгоритм бэктрекинга: n ферзей"""
|
||||
# Когда все строки уже обработаны, записать решение
|
||||
if row == n:
|
||||
res.append([list(row) for row in state])
|
||||
return
|
||||
# Обойти все столбцы
|
||||
for col in range(n):
|
||||
# Вычислить главную и побочную диагонали, соответствующие этой клетке
|
||||
diag1 = row - col + n - 1
|
||||
diag2 = row + col
|
||||
# Отсечение: в столбце, главной диагонали и побочной диагонали этой клетки не должно быть ферзей
|
||||
if not cols[col] and not diags1[diag1] and not diags2[diag2]:
|
||||
# Попытка: поставить ферзя в эту клетку
|
||||
state[row][col] = "Q"
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = True
|
||||
# Перейти к размещению следующей строки
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2)
|
||||
# Откат: восстановить эту клетку как пустую
|
||||
state[row][col] = "#"
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = False
|
||||
|
||||
|
||||
def n_queens(n: int) -> list[list[list[str]]]:
|
||||
"""Решить задачу о n ферзях"""
|
||||
# Инициализировать доску размера n*n, где 'Q' обозначает ферзя, а '#' — пустую клетку
|
||||
state = [["#" for _ in range(n)] for _ in range(n)]
|
||||
cols = [False] * n # Отмечать, есть ли ферзь в столбце
|
||||
diags1 = [False] * (2 * n - 1) # Отмечать наличие ферзя на главной диагонали
|
||||
diags2 = [False] * (2 * n - 1) # Отмечать наличие ферзя на побочной диагонали
|
||||
res = []
|
||||
backtrack(0, n, state, res, cols, diags1, diags2)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 4
|
||||
res = n_queens(n)
|
||||
|
||||
print(f"Размер входной доски = {n}")
|
||||
print(f"Количество способов расстановки ферзей: {len(res)}")
|
||||
for state in res:
|
||||
print("--------------------")
|
||||
for row in state:
|
||||
print(row)
|
||||
44
ru/codes/python/chapter_backtracking/permutations_i.py
Normal file
44
ru/codes/python/chapter_backtracking/permutations_i.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
File: permutations_i.py
|
||||
Created Time: 2023-04-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def backtrack(
|
||||
state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]
|
||||
):
|
||||
"""Алгоритм бэктрекинга: все перестановки I"""
|
||||
# Когда длина состояния равна числу элементов, записать решение
|
||||
if len(state) == len(choices):
|
||||
res.append(list(state))
|
||||
return
|
||||
# Перебор всех вариантов выбора
|
||||
for i, choice in enumerate(choices):
|
||||
# Отсечение: нельзя выбирать один и тот же элемент повторно
|
||||
if not selected[i]:
|
||||
# Попытка: сделать выбор и обновить состояние
|
||||
selected[i] = True
|
||||
state.append(choice)
|
||||
# Перейти к следующему выбору
|
||||
backtrack(state, choices, selected, res)
|
||||
# Откат: отменить выбор и восстановить предыдущее состояние
|
||||
selected[i] = False
|
||||
state.pop()
|
||||
|
||||
|
||||
def permutations_i(nums: list[int]) -> list[list[int]]:
|
||||
"""Все перестановки I"""
|
||||
res = []
|
||||
backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [1, 2, 3]
|
||||
|
||||
res = permutations_i(nums)
|
||||
|
||||
print(f"Входной массив nums = {nums}")
|
||||
print(f"Все перестановки res = {res}")
|
||||
46
ru/codes/python/chapter_backtracking/permutations_ii.py
Normal file
46
ru/codes/python/chapter_backtracking/permutations_ii.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""
|
||||
File: permutations_ii.py
|
||||
Created Time: 2023-04-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def backtrack(
|
||||
state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]
|
||||
):
|
||||
"""Алгоритм бэктрекинга: все перестановки II"""
|
||||
# Когда длина состояния равна числу элементов, записать решение
|
||||
if len(state) == len(choices):
|
||||
res.append(list(state))
|
||||
return
|
||||
# Перебор всех вариантов выбора
|
||||
duplicated = set[int]()
|
||||
for i, choice in enumerate(choices):
|
||||
# Отсечение: нельзя выбирать один и тот же элемент повторно и нельзя повторно выбирать равные элементы
|
||||
if not selected[i] and choice not in duplicated:
|
||||
# Попытка: сделать выбор и обновить состояние
|
||||
duplicated.add(choice) # Записать значения уже выбранных элементов
|
||||
selected[i] = True
|
||||
state.append(choice)
|
||||
# Перейти к следующему выбору
|
||||
backtrack(state, choices, selected, res)
|
||||
# Откат: отменить выбор и восстановить предыдущее состояние
|
||||
selected[i] = False
|
||||
state.pop()
|
||||
|
||||
|
||||
def permutations_ii(nums: list[int]) -> list[list[int]]:
|
||||
"""Все перестановки II"""
|
||||
res = []
|
||||
backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [1, 2, 2]
|
||||
|
||||
res = permutations_ii(nums)
|
||||
|
||||
print(f"Входной массив nums = {nums}")
|
||||
print(f"Все перестановки res = {res}")
|
||||
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
File: preorder_traversal_i_compact.py
|
||||
Created Time: 2023-04-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree, list_to_tree
|
||||
|
||||
|
||||
def pre_order(root: TreeNode):
|
||||
"""Предварительный обход: пример 1"""
|
||||
if root is None:
|
||||
return
|
||||
if root.val == 7:
|
||||
# Записать решение
|
||||
res.append(root)
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
root = list_to_tree([1, 7, 3, 4, 5, 6, 7])
|
||||
print("\nИнициализация двоичного дерева")
|
||||
print_tree(root)
|
||||
|
||||
# Предварительный обход
|
||||
res = list[TreeNode]()
|
||||
pre_order(root)
|
||||
|
||||
print("\nВсе узлы со значением 7")
|
||||
print([node.val for node in res])
|
||||
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
File: preorder_traversal_ii_compact.py
|
||||
Created Time: 2023-04-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree, list_to_tree
|
||||
|
||||
|
||||
def pre_order(root: TreeNode):
|
||||
"""Предварительный обход: пример 2"""
|
||||
if root is None:
|
||||
return
|
||||
# Попытка
|
||||
path.append(root)
|
||||
if root.val == 7:
|
||||
# Записать решение
|
||||
res.append(list(path))
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
# Откат
|
||||
path.pop()
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
root = list_to_tree([1, 7, 3, 4, 5, 6, 7])
|
||||
print("\nИнициализация двоичного дерева")
|
||||
print_tree(root)
|
||||
|
||||
# Предварительный обход
|
||||
path = list[TreeNode]()
|
||||
res = list[list[TreeNode]]()
|
||||
pre_order(root)
|
||||
|
||||
print("\nВсе пути от корня к узлу 7")
|
||||
for path in res:
|
||||
print([node.val for node in path])
|
||||
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
File: preorder_traversal_iii_compact.py
|
||||
Created Time: 2023-04-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree, list_to_tree
|
||||
|
||||
|
||||
def pre_order(root: TreeNode):
|
||||
"""Предварительный обход: пример 3"""
|
||||
# Отсечение
|
||||
if root is None or root.val == 3:
|
||||
return
|
||||
# Попытка
|
||||
path.append(root)
|
||||
if root.val == 7:
|
||||
# Записать решение
|
||||
res.append(list(path))
|
||||
pre_order(root.left)
|
||||
pre_order(root.right)
|
||||
# Откат
|
||||
path.pop()
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
root = list_to_tree([1, 7, 3, 4, 5, 6, 7])
|
||||
print("\nИнициализация двоичного дерева")
|
||||
print_tree(root)
|
||||
|
||||
# Предварительный обход
|
||||
path = list[TreeNode]()
|
||||
res = list[list[TreeNode]]()
|
||||
pre_order(root)
|
||||
|
||||
print("\nВсе пути от корня к узлу 7, не содержащие узлов со значением 3")
|
||||
for path in res:
|
||||
print([node.val for node in path])
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
File: preorder_traversal_iii_template.py
|
||||
Created Time: 2023-04-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree, list_to_tree
|
||||
|
||||
|
||||
def is_solution(state: list[TreeNode]) -> bool:
|
||||
"""Проверить, является ли текущее состояние решением"""
|
||||
return state and state[-1].val == 7
|
||||
|
||||
|
||||
def record_solution(state: list[TreeNode], res: list[list[TreeNode]]):
|
||||
"""Записать решение"""
|
||||
res.append(list(state))
|
||||
|
||||
|
||||
def is_valid(state: list[TreeNode], choice: TreeNode) -> bool:
|
||||
"""Проверить, допустим ли этот выбор в текущем состоянии"""
|
||||
return choice is not None and choice.val != 3
|
||||
|
||||
|
||||
def make_choice(state: list[TreeNode], choice: TreeNode):
|
||||
"""Обновить состояние"""
|
||||
state.append(choice)
|
||||
|
||||
|
||||
def undo_choice(state: list[TreeNode], choice: TreeNode):
|
||||
"""Восстановить состояние"""
|
||||
state.pop()
|
||||
|
||||
|
||||
def backtrack(
|
||||
state: list[TreeNode], choices: list[TreeNode], res: list[list[TreeNode]]
|
||||
):
|
||||
"""Алгоритм бэктрекинга: пример 3"""
|
||||
# Проверить, является ли текущее состояние решением
|
||||
if is_solution(state):
|
||||
# Записать решение
|
||||
record_solution(state, res)
|
||||
# Перебор всех вариантов выбора
|
||||
for choice in choices:
|
||||
# Отсечение: проверить допустимость выбора
|
||||
if is_valid(state, choice):
|
||||
# Попытка: сделать выбор и обновить состояние
|
||||
make_choice(state, choice)
|
||||
# Перейти к следующему выбору
|
||||
backtrack(state, [choice.left, choice.right], res)
|
||||
# Откат: отменить выбор и восстановить предыдущее состояние
|
||||
undo_choice(state, choice)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
root = list_to_tree([1, 7, 3, 4, 5, 6, 7])
|
||||
print("\nИнициализация двоичного дерева")
|
||||
print_tree(root)
|
||||
|
||||
# Алгоритм бэктрекинга
|
||||
res = []
|
||||
backtrack(state=[], choices=[root], res=res)
|
||||
|
||||
print("\nВсе пути от корня к узлу 7, в которых путь не содержит узлов со значением 3")
|
||||
for path in res:
|
||||
print([node.val for node in path])
|
||||
48
ru/codes/python/chapter_backtracking/subset_sum_i.py
Normal file
48
ru/codes/python/chapter_backtracking/subset_sum_i.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""
|
||||
File: subset_sum_i.py
|
||||
Created Time: 2023-06-17
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def backtrack(
|
||||
state: list[int], target: int, choices: list[int], start: int, res: list[list[int]]
|
||||
):
|
||||
"""Алгоритм бэктрекинга: сумма подмножеств I"""
|
||||
# Если сумма подмножества равна target, записать решение
|
||||
if target == 0:
|
||||
res.append(list(state))
|
||||
return
|
||||
# Обойти все варианты выбора
|
||||
# Отсечение 2: начинать обход с start, чтобы избежать генерации повторяющихся подмножеств
|
||||
for i in range(start, len(choices)):
|
||||
# Отсечение 1: если сумма подмножества превышает target, немедленно завершить цикл
|
||||
# Это связано с тем, что массив уже отсортирован, следующие элементы больше, и сумма подмножества точно превысит target
|
||||
if target - choices[i] < 0:
|
||||
break
|
||||
# Попытка: сделать выбор и обновить target и start
|
||||
state.append(choices[i])
|
||||
# Перейти к следующему выбору
|
||||
backtrack(state, target - choices[i], choices, i, res)
|
||||
# Откат: отменить выбор и восстановить предыдущее состояние
|
||||
state.pop()
|
||||
|
||||
|
||||
def subset_sum_i(nums: list[int], target: int) -> list[list[int]]:
|
||||
"""Решить задачу суммы подмножеств I"""
|
||||
state = [] # Состояние (подмножество)
|
||||
nums.sort() # Отсортировать nums
|
||||
start = 0 # Стартовая вершина обхода
|
||||
res = [] # Список результатов (список подмножеств)
|
||||
backtrack(state, target, nums, start, res)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [3, 4, 5]
|
||||
target = 9
|
||||
res = subset_sum_i(nums, target)
|
||||
|
||||
print(f"Входной массив nums = {nums}, target = {target}")
|
||||
print(f"Все подмножества с суммой {target}: res = {res}")
|
||||
50
ru/codes/python/chapter_backtracking/subset_sum_i_naive.py
Normal file
50
ru/codes/python/chapter_backtracking/subset_sum_i_naive.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
File: subset_sum_i_naive.py
|
||||
Created Time: 2023-06-17
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def backtrack(
|
||||
state: list[int],
|
||||
target: int,
|
||||
total: int,
|
||||
choices: list[int],
|
||||
res: list[list[int]],
|
||||
):
|
||||
"""Алгоритм бэктрекинга: сумма подмножеств I"""
|
||||
# Если сумма подмножества равна target, записать решение
|
||||
if total == target:
|
||||
res.append(list(state))
|
||||
return
|
||||
# Перебор всех вариантов выбора
|
||||
for i in range(len(choices)):
|
||||
# Отсечение: если сумма подмножества превышает target, пропустить этот выбор
|
||||
if total + choices[i] > target:
|
||||
continue
|
||||
# Попытка: сделать выбор и обновить элемент и total
|
||||
state.append(choices[i])
|
||||
# Перейти к следующему выбору
|
||||
backtrack(state, target, total + choices[i], choices, res)
|
||||
# Откат: отменить выбор и восстановить предыдущее состояние
|
||||
state.pop()
|
||||
|
||||
|
||||
def subset_sum_i_naive(nums: list[int], target: int) -> list[list[int]]:
|
||||
"""Решить задачу суммы подмножеств I (с повторяющимися подмножествами)"""
|
||||
state = [] # Состояние (подмножество)
|
||||
total = 0 # Сумма подмножеств
|
||||
res = [] # Список результатов (список подмножеств)
|
||||
backtrack(state, target, total, nums, res)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [3, 4, 5]
|
||||
target = 9
|
||||
res = subset_sum_i_naive(nums, target)
|
||||
|
||||
print(f"Входной массив nums = {nums}, target = {target}")
|
||||
print(f"Все подмножества с суммой {target}: res = {res}")
|
||||
print(f"Обратите внимание: результат этого метода содержит повторяющиеся множества")
|
||||
52
ru/codes/python/chapter_backtracking/subset_sum_ii.py
Normal file
52
ru/codes/python/chapter_backtracking/subset_sum_ii.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
File: subset_sum_ii.py
|
||||
Created Time: 2023-06-17
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def backtrack(
|
||||
state: list[int], target: int, choices: list[int], start: int, res: list[list[int]]
|
||||
):
|
||||
"""Алгоритм бэктрекинга: сумма подмножеств II"""
|
||||
# Если сумма подмножества равна target, записать решение
|
||||
if target == 0:
|
||||
res.append(list(state))
|
||||
return
|
||||
# Обойти все варианты выбора
|
||||
# Отсечение 2: начинать обход с start, чтобы избежать генерации повторяющихся подмножеств
|
||||
# Отсечение 3: начинать обход с start, чтобы избежать повторного выбора одного и того же элемента
|
||||
for i in range(start, len(choices)):
|
||||
# Отсечение 1: если сумма подмножества превышает target, немедленно завершить цикл
|
||||
# Это связано с тем, что массив уже отсортирован, следующие элементы больше, и сумма подмножества точно превысит target
|
||||
if target - choices[i] < 0:
|
||||
break
|
||||
# Отсечение 4: если этот элемент равен элементу слева, значит ветвь поиска повторяется, ее нужно сразу пропустить
|
||||
if i > start and choices[i] == choices[i - 1]:
|
||||
continue
|
||||
# Попытка: сделать выбор и обновить target и start
|
||||
state.append(choices[i])
|
||||
# Перейти к следующему выбору
|
||||
backtrack(state, target - choices[i], choices, i + 1, res)
|
||||
# Откат: отменить выбор и восстановить предыдущее состояние
|
||||
state.pop()
|
||||
|
||||
|
||||
def subset_sum_ii(nums: list[int], target: int) -> list[list[int]]:
|
||||
"""Решить задачу суммы подмножеств II"""
|
||||
state = [] # Состояние (подмножество)
|
||||
nums.sort() # Отсортировать nums
|
||||
start = 0 # Стартовая вершина обхода
|
||||
res = [] # Список результатов (список подмножеств)
|
||||
backtrack(state, target, nums, start, res)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [4, 4, 5]
|
||||
target = 9
|
||||
res = subset_sum_ii(nums, target)
|
||||
|
||||
print(f"Входной массив nums = {nums}, target = {target}")
|
||||
print(f"Все подмножества с суммой {target}: res = {res}")
|
||||
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
File: iteration.py
|
||||
Created Time: 2023-08-24
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def for_loop(n: int) -> int:
|
||||
"""Цикл for"""
|
||||
res = 0
|
||||
# Циклическое суммирование 1, 2, ..., n-1, n
|
||||
for i in range(1, n + 1):
|
||||
res += i
|
||||
return res
|
||||
|
||||
|
||||
def while_loop(n: int) -> int:
|
||||
"""Цикл while"""
|
||||
res = 0
|
||||
i = 1 # Инициализация условной переменной
|
||||
# Циклическое суммирование 1, 2, ..., n-1, n
|
||||
while i <= n:
|
||||
res += i
|
||||
i += 1 # Обновить условную переменную
|
||||
return res
|
||||
|
||||
|
||||
def while_loop_ii(n: int) -> int:
|
||||
"""Цикл while (двойное обновление)"""
|
||||
res = 0
|
||||
i = 1 # Инициализация условной переменной
|
||||
# Циклическое суммирование 1, 4, 10, ...
|
||||
while i <= n:
|
||||
res += i
|
||||
# Обновить условную переменную
|
||||
i += 1
|
||||
i *= 2
|
||||
return res
|
||||
|
||||
|
||||
def nested_for_loop(n: int) -> str:
|
||||
"""Двойной цикл for"""
|
||||
res = ""
|
||||
# Цикл по i = 1, 2, ..., n-1, n
|
||||
for i in range(1, n + 1):
|
||||
# Цикл по j = 1, 2, ..., n-1, n
|
||||
for j in range(1, n + 1):
|
||||
res += f"({i}, {j}), "
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 5
|
||||
res = for_loop(n)
|
||||
print(f"\nРезультат суммирования в цикле for res = {res}")
|
||||
|
||||
res = while_loop(n)
|
||||
print(f"\nРезультат суммирования в цикле while res = {res}")
|
||||
|
||||
res = while_loop_ii(n)
|
||||
print(f"\nРезультат суммирования в цикле while (двойное обновление) res = {res}")
|
||||
|
||||
res = nested_for_loop(n)
|
||||
print(f"\nРезультат обхода в двойном цикле for {res}")
|
||||
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
File: recursion.py
|
||||
Created Time: 2023-08-24
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def recur(n: int) -> int:
|
||||
"""Рекурсия"""
|
||||
# Условие завершения
|
||||
if n == 1:
|
||||
return 1
|
||||
# Рекурсия: рекурсивный вызов
|
||||
res = recur(n - 1)
|
||||
# Возврат: вернуть результат
|
||||
return n + res
|
||||
|
||||
|
||||
def for_loop_recur(n: int) -> int:
|
||||
"""Имитация рекурсии итерацией"""
|
||||
# Использовать явный стек для имитации системного стека вызовов
|
||||
stack = []
|
||||
res = 0
|
||||
# Рекурсия: рекурсивный вызов
|
||||
for i in range(n, 0, -1):
|
||||
# Имитировать «рекурсию» с помощью операции помещения в стек
|
||||
stack.append(i)
|
||||
# Возврат: вернуть результат
|
||||
while stack:
|
||||
# Имитировать «возврат» с помощью операции извлечения из стека
|
||||
res += stack.pop()
|
||||
# res = 1+2+3+...+n
|
||||
return res
|
||||
|
||||
|
||||
def tail_recur(n, res):
|
||||
"""Хвостовая рекурсия"""
|
||||
# Условие завершения
|
||||
if n == 0:
|
||||
return res
|
||||
# Хвостовой рекурсивный вызов
|
||||
return tail_recur(n - 1, res + n)
|
||||
|
||||
|
||||
def fib(n: int) -> int:
|
||||
"""Последовательность Фибоначчи: рекурсия"""
|
||||
# Условие завершения: f(1) = 0, f(2) = 1
|
||||
if n == 1 or n == 2:
|
||||
return n - 1
|
||||
# Рекурсивный вызов f(n) = f(n-1) + f(n-2)
|
||||
res = fib(n - 1) + fib(n - 2)
|
||||
# Вернуть результат f(n)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 5
|
||||
res = recur(n)
|
||||
print(f"\nРезультат суммирования в рекурсивной функции res = {res}")
|
||||
|
||||
res = for_loop_recur(n)
|
||||
print(f"\nРезультат суммирования при имитации рекурсии res = {res}")
|
||||
|
||||
res = tail_recur(n, 0)
|
||||
print(f"\nРезультат суммирования в хвостовой рекурсии res = {res}")
|
||||
|
||||
res = fib(n)
|
||||
print(f"\nЧлен последовательности Фибоначчи с номером {n} = {res}")
|
||||
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
File: space_complexity.py
|
||||
Created Time: 2022-11-25
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import ListNode, TreeNode, print_tree
|
||||
|
||||
|
||||
def function() -> int:
|
||||
"""Функция"""
|
||||
# Выполнить некоторые операции
|
||||
return 0
|
||||
|
||||
|
||||
def constant(n: int):
|
||||
"""Постоянная сложность"""
|
||||
# Константы, переменные и объекты занимают O(1) памяти
|
||||
a = 0
|
||||
nums = [0] * 10000
|
||||
node = ListNode(0)
|
||||
# Переменные в цикле занимают O(1) памяти
|
||||
for _ in range(n):
|
||||
c = 0
|
||||
# Функции в цикле занимают O(1) памяти
|
||||
for _ in range(n):
|
||||
function()
|
||||
|
||||
|
||||
def linear(n: int):
|
||||
"""Линейная сложность"""
|
||||
# Список длины n занимает O(n) памяти
|
||||
nums = [0] * n
|
||||
# Хеш-таблица длины n занимает O(n) памяти
|
||||
hmap = dict[int, str]()
|
||||
for i in range(n):
|
||||
hmap[i] = str(i)
|
||||
|
||||
|
||||
def linear_recur(n: int):
|
||||
"""Линейная сложность (рекурсивная реализация)"""
|
||||
print("Рекурсия n =", n)
|
||||
if n == 1:
|
||||
return
|
||||
linear_recur(n - 1)
|
||||
|
||||
|
||||
def quadratic(n: int):
|
||||
"""Квадратичная сложность"""
|
||||
# Двумерный список занимает O(n^2) памяти
|
||||
num_matrix = [[0] * n for _ in range(n)]
|
||||
|
||||
|
||||
def quadratic_recur(n: int) -> int:
|
||||
"""Квадратичная сложность (рекурсивная реализация)"""
|
||||
if n <= 0:
|
||||
return 0
|
||||
# Длина массива nums равна n, n-1, ..., 2, 1
|
||||
nums = [0] * n
|
||||
return quadratic_recur(n - 1)
|
||||
|
||||
|
||||
def build_tree(n: int) -> TreeNode | None:
|
||||
"""Экспоненциальная сложность (построение полного двоичного дерева)"""
|
||||
if n == 0:
|
||||
return None
|
||||
root = TreeNode(0)
|
||||
root.left = build_tree(n - 1)
|
||||
root.right = build_tree(n - 1)
|
||||
return root
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 5
|
||||
# Постоянная сложность
|
||||
constant(n)
|
||||
# Линейная сложность
|
||||
linear(n)
|
||||
linear_recur(n)
|
||||
# Квадратичная сложность
|
||||
quadratic(n)
|
||||
quadratic_recur(n)
|
||||
# Экспоненциальная сложность
|
||||
root = build_tree(n)
|
||||
print_tree(root)
|
||||
@@ -0,0 +1,153 @@
|
||||
"""
|
||||
File: time_complexity.py
|
||||
Created Time: 2022-11-25
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def constant(n: int) -> int:
|
||||
"""Постоянная сложность"""
|
||||
count = 0
|
||||
size = 100000
|
||||
for _ in range(size):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def linear(n: int) -> int:
|
||||
"""Линейная сложность"""
|
||||
count = 0
|
||||
for _ in range(n):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def array_traversal(nums: list[int]) -> int:
|
||||
"""Линейная сложность (обход массива)"""
|
||||
count = 0
|
||||
# Число итераций пропорционально длине массива
|
||||
for num in nums:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def quadratic(n: int) -> int:
|
||||
"""Квадратичная сложность"""
|
||||
count = 0
|
||||
# Число итераций квадратично зависит от размера данных n
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def bubble_sort(nums: list[int]) -> int:
|
||||
"""Квадратичная сложность (пузырьковая сортировка)"""
|
||||
count = 0 # Счетчик
|
||||
# Внешний цикл: неотсортированный диапазон [0, i]
|
||||
for i in range(len(nums) - 1, 0, -1):
|
||||
# Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
|
||||
for j in range(i):
|
||||
if nums[j] > nums[j + 1]:
|
||||
# Поменять местами nums[j] и nums[j + 1]
|
||||
tmp: int = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = tmp
|
||||
count += 3 # Обмен элементов включает 3 элементарные операции
|
||||
return count
|
||||
|
||||
|
||||
def exponential(n: int) -> int:
|
||||
"""Экспоненциальная сложность (итеративная реализация)"""
|
||||
count = 0
|
||||
base = 1
|
||||
# На каждом шаге клетка делится надвое, образуя последовательность 1, 2, 4, 8, ..., 2^(n-1)
|
||||
for _ in range(n):
|
||||
for _ in range(base):
|
||||
count += 1
|
||||
base *= 2
|
||||
# count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1
|
||||
return count
|
||||
|
||||
|
||||
def exp_recur(n: int) -> int:
|
||||
"""Экспоненциальная сложность (рекурсивная реализация)"""
|
||||
if n == 1:
|
||||
return 1
|
||||
return exp_recur(n - 1) + exp_recur(n - 1) + 1
|
||||
|
||||
|
||||
def logarithmic(n: int) -> int:
|
||||
"""Логарифмическая сложность (итеративная реализация)"""
|
||||
count = 0
|
||||
while n > 1:
|
||||
n = n / 2
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def log_recur(n: int) -> int:
|
||||
"""Логарифмическая сложность (рекурсивная реализация)"""
|
||||
if n <= 1:
|
||||
return 0
|
||||
return log_recur(n / 2) + 1
|
||||
|
||||
|
||||
def linear_log_recur(n: int) -> int:
|
||||
"""Линейно-логарифмическая сложность"""
|
||||
if n <= 1:
|
||||
return 1
|
||||
# Разделение надвое: размер подзадачи уменьшается вдвое
|
||||
count = linear_log_recur(n // 2) + linear_log_recur(n // 2)
|
||||
# Текущая подзадача содержит n операций
|
||||
for _ in range(n):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def factorial_recur(n: int) -> int:
|
||||
"""Факториальная сложность (рекурсивная реализация)"""
|
||||
if n == 0:
|
||||
return 1
|
||||
count = 0
|
||||
# Из одного получается n
|
||||
for _ in range(n):
|
||||
count += factorial_recur(n - 1)
|
||||
return count
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Можно изменить n и запустить программу, чтобы увидеть, как меняется число операций при разных сложностях
|
||||
n = 8
|
||||
print("Размер входных данных n =", n)
|
||||
|
||||
count = constant(n)
|
||||
print("Число операций константной сложности =", count)
|
||||
|
||||
count = linear(n)
|
||||
print("Число операций линейной сложности =", count)
|
||||
count = array_traversal([0] * n)
|
||||
print("Число операций линейной сложности (обход массива) =", count)
|
||||
|
||||
count = quadratic(n)
|
||||
print("Число операций квадратичной сложности =", count)
|
||||
nums = [i for i in range(n, 0, -1)] # [n, n-1, ..., 2, 1]
|
||||
count = bubble_sort(nums)
|
||||
print("Число операций квадратичной сложности (пузырьковая сортировка) =", count)
|
||||
|
||||
count = exponential(n)
|
||||
print("Число операций экспоненциальной сложности (итеративная реализация) =", count)
|
||||
count = exp_recur(n)
|
||||
print("Число операций экспоненциальной сложности (рекурсивная реализация) =", count)
|
||||
|
||||
count = logarithmic(n)
|
||||
print("Число операций логарифмической сложности (итеративная реализация) =", count)
|
||||
count = log_recur(n)
|
||||
print("Число операций логарифмической сложности (рекурсивная реализация) =", count)
|
||||
|
||||
count = linear_log_recur(n)
|
||||
print("Число операций линейно-логарифмической сложности (рекурсивная реализация) =", count)
|
||||
|
||||
count = factorial_recur(n)
|
||||
print("Число операций факториальной сложности (рекурсивная реализация) =", count)
|
||||
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
File: worst_best_time_complexity.py
|
||||
Created Time: 2022-11-25
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
|
||||
def random_numbers(n: int) -> list[int]:
|
||||
"""Сгенерировать массив с элементами 1, 2, ..., n в случайном порядке"""
|
||||
# Создать массив nums =: 1, 2, 3, ..., n
|
||||
nums = [i for i in range(1, n + 1)]
|
||||
# Случайно перемешать элементы массива
|
||||
random.shuffle(nums)
|
||||
return nums
|
||||
|
||||
|
||||
def find_one(nums: list[int]) -> int:
|
||||
"""Найти индекс числа 1 в массиве nums"""
|
||||
for i in range(len(nums)):
|
||||
# Когда элемент 1 находится в начале массива, достигается лучшая временная сложность O(1)
|
||||
# Когда элемент 1 находится в конце массива, достигается худшая временная сложность O(n)
|
||||
if nums[i] == 1:
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
for i in range(10):
|
||||
n = 100
|
||||
nums: list[int] = random_numbers(n)
|
||||
index: int = find_one(nums)
|
||||
print("\nМассив [1, 2, ..., n] после перемешивания =", nums)
|
||||
print("Индекс числа 1 =", index)
|
||||
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
File: binary_search_recur.py
|
||||
Created Time: 2023-07-17
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def dfs(nums: list[int], target: int, i: int, j: int) -> int:
|
||||
"""Бинарный поиск: задача f(i, j)"""
|
||||
# Если интервал пуст, целевой элемент отсутствует, вернуть -1
|
||||
if i > j:
|
||||
return -1
|
||||
# Вычислить индекс середины m
|
||||
m = (i + j) // 2
|
||||
if nums[m] < target:
|
||||
# Рекурсивная подзадача f(m+1, j)
|
||||
return dfs(nums, target, m + 1, j)
|
||||
elif nums[m] > target:
|
||||
# Рекурсивная подзадача f(i, m-1)
|
||||
return dfs(nums, target, i, m - 1)
|
||||
else:
|
||||
# Целевой элемент найден, вернуть его индекс
|
||||
return m
|
||||
|
||||
|
||||
def binary_search(nums: list[int], target: int) -> int:
|
||||
"""Бинарный поиск"""
|
||||
n = len(nums)
|
||||
# Решить задачу f(0, n-1)
|
||||
return dfs(nums, target, 0, n - 1)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
target = 6
|
||||
nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
|
||||
|
||||
# Бинарный поиск (двусторонне замкнутый интервал)
|
||||
index = binary_search(nums, target)
|
||||
print("Индекс целевого элемента 6 = ", index)
|
||||
54
ru/codes/python/chapter_divide_and_conquer/build_tree.py
Normal file
54
ru/codes/python/chapter_divide_and_conquer/build_tree.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
File: build_tree.py
|
||||
Created Time: 2023-07-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree
|
||||
|
||||
|
||||
def dfs(
|
||||
preorder: list[int],
|
||||
inorder_map: dict[int, int],
|
||||
i: int,
|
||||
l: int,
|
||||
r: int,
|
||||
) -> TreeNode | None:
|
||||
"""Построить двоичное дерево: разделяй и властвуй"""
|
||||
# Завершить при пустом диапазоне поддерева
|
||||
if r - l < 0:
|
||||
return None
|
||||
# Инициализировать корневой узел
|
||||
root = TreeNode(preorder[i])
|
||||
# Найти m, чтобы разделить левое и правое поддеревья
|
||||
m = inorder_map[preorder[i]]
|
||||
# Подзадача: построить левое поддерево
|
||||
root.left = dfs(preorder, inorder_map, i + 1, l, m - 1)
|
||||
# Подзадача: построить правое поддерево
|
||||
root.right = dfs(preorder, inorder_map, i + 1 + m - l, m + 1, r)
|
||||
# Вернуть корневой узел
|
||||
return root
|
||||
|
||||
|
||||
def build_tree(preorder: list[int], inorder: list[int]) -> TreeNode | None:
|
||||
"""Построить двоичное дерево"""
|
||||
# Инициализировать хеш-таблицу для хранения соответствия элементов inorder их индексам
|
||||
inorder_map = {val: i for i, val in enumerate(inorder)}
|
||||
root = dfs(preorder, inorder_map, 0, 0, len(inorder) - 1)
|
||||
return root
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
preorder = [3, 9, 2, 1, 7]
|
||||
inorder = [9, 3, 1, 2, 7]
|
||||
print(f"Предварительный обход = {preorder}")
|
||||
print(f"Симметричный обход = {inorder}")
|
||||
|
||||
root = build_tree(preorder, inorder)
|
||||
print("Построенное двоичное дерево:")
|
||||
print_tree(root)
|
||||
53
ru/codes/python/chapter_divide_and_conquer/hanota.py
Normal file
53
ru/codes/python/chapter_divide_and_conquer/hanota.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
File: hanota.py
|
||||
Created Time: 2023-07-16
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def move(src: list[int], tar: list[int]):
|
||||
"""Переместить один диск"""
|
||||
# Снять диск с вершины src
|
||||
pan = src.pop()
|
||||
# Положить диск на вершину tar
|
||||
tar.append(pan)
|
||||
|
||||
|
||||
def dfs(i: int, src: list[int], buf: list[int], tar: list[int]):
|
||||
"""Решить задачу Ханойской башни f(i)"""
|
||||
# Если в src остался только один диск, сразу переместить его в tar
|
||||
if i == 1:
|
||||
move(src, tar)
|
||||
return
|
||||
# Подзадача f(i-1): переместить верхние i-1 дисков из src в buf с помощью tar
|
||||
dfs(i - 1, src, tar, buf)
|
||||
# Подзадача f(1): переместить оставшийся один диск из src в tar
|
||||
move(src, tar)
|
||||
# Подзадача f(i-1): переместить верхние i-1 дисков из buf в tar с помощью src
|
||||
dfs(i - 1, buf, src, tar)
|
||||
|
||||
|
||||
def solve_hanota(A: list[int], B: list[int], C: list[int]):
|
||||
"""Решить задачу Ханойской башни"""
|
||||
n = len(A)
|
||||
# Переместить верхние n дисков из A в C с помощью B
|
||||
dfs(n, A, B, C)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Хвост списка соответствует вершине столбца
|
||||
A = [5, 4, 3, 2, 1]
|
||||
B = []
|
||||
C = []
|
||||
print("Исходное состояние:")
|
||||
print(f"A = {A}")
|
||||
print(f"B = {B}")
|
||||
print(f"C = {C}")
|
||||
|
||||
solve_hanota(A, B, C)
|
||||
|
||||
print("После завершения перемещения дисков:")
|
||||
print(f"A = {A}")
|
||||
print(f"B = {B}")
|
||||
print(f"C = {C}")
|
||||
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
File: climbing_stairs_backtrack.py
|
||||
Created Time: 2023-06-30
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def backtrack(choices: list[int], state: int, n: int, res: list[int]) -> int:
|
||||
"""Бэктрекинг"""
|
||||
# Когда подъем достигает n-й ступени, число вариантов увеличивается на 1
|
||||
if state == n:
|
||||
res[0] += 1
|
||||
# Перебор всех вариантов выбора
|
||||
for choice in choices:
|
||||
# Отсечение: нельзя выходить за n-ю ступень
|
||||
if state + choice > n:
|
||||
continue
|
||||
# Попытка: сделать выбор и обновить состояние
|
||||
backtrack(choices, state + choice, n, res)
|
||||
# Откат
|
||||
|
||||
|
||||
def climbing_stairs_backtrack(n: int) -> int:
|
||||
"""Подъем по лестнице: бэктрекинг"""
|
||||
choices = [1, 2] # Можно подняться на 1 или 2 ступени
|
||||
state = 0 # Начать подъем с 0-й ступени
|
||||
res = [0] # Использовать res[0] для хранения числа решений
|
||||
backtrack(choices, state, n, res)
|
||||
return res[0]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 9
|
||||
|
||||
res = climbing_stairs_backtrack(n)
|
||||
print(f"Количество способов подняться по лестнице из {n} ступеней = {res}")
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
File: climbing_stairs_constraint_dp.py
|
||||
Created Time: 2023-06-30
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def climbing_stairs_constraint_dp(n: int) -> int:
|
||||
"""Подъем по лестнице с ограничениями: динамическое программирование"""
|
||||
if n == 1 or n == 2:
|
||||
return 1
|
||||
# Инициализация таблицы dp для хранения решений подзадач
|
||||
dp = [[0] * 3 for _ in range(n + 1)]
|
||||
# Начальное состояние: заранее задать решения наименьших подзадач
|
||||
dp[1][1], dp[1][2] = 1, 0
|
||||
dp[2][1], dp[2][2] = 0, 1
|
||||
# Переход состояний: постепенное решение больших подзадач через меньшие
|
||||
for i in range(3, n + 1):
|
||||
dp[i][1] = dp[i - 1][2]
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2]
|
||||
return dp[n][1] + dp[n][2]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 9
|
||||
|
||||
res = climbing_stairs_constraint_dp(n)
|
||||
print(f"Количество способов подняться по лестнице из {n} ступеней = {res}")
|
||||
@@ -0,0 +1,28 @@
|
||||
"""
|
||||
File: climbing_stairs_dfs.py
|
||||
Created Time: 2023-06-30
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def dfs(i: int) -> int:
|
||||
"""Поиск"""
|
||||
# dp[1] и dp[2] уже известны, вернуть их
|
||||
if i == 1 or i == 2:
|
||||
return i
|
||||
# dp[i] = dp[i-1] + dp[i-2]
|
||||
count = dfs(i - 1) + dfs(i - 2)
|
||||
return count
|
||||
|
||||
|
||||
def climbing_stairs_dfs(n: int) -> int:
|
||||
"""Подъем по лестнице: поиск"""
|
||||
return dfs(n)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 9
|
||||
|
||||
res = climbing_stairs_dfs(n)
|
||||
print(f"Количество способов подняться по лестнице из {n} ступеней = {res}")
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
File: climbing_stairs_dfs_mem.py
|
||||
Created Time: 2023-06-30
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def dfs(i: int, mem: list[int]) -> int:
|
||||
"""Поиск с мемоизацией"""
|
||||
# dp[1] и dp[2] уже известны, вернуть их
|
||||
if i == 1 or i == 2:
|
||||
return i
|
||||
# Если запись dp[i] существует, сразу вернуть ее
|
||||
if mem[i] != -1:
|
||||
return mem[i]
|
||||
# dp[i] = dp[i-1] + dp[i-2]
|
||||
count = dfs(i - 1, mem) + dfs(i - 2, mem)
|
||||
# Сохранить dp[i]
|
||||
mem[i] = count
|
||||
return count
|
||||
|
||||
|
||||
def climbing_stairs_dfs_mem(n: int) -> int:
|
||||
"""Подъем по лестнице: поиск с мемоизацией"""
|
||||
# mem[i] хранит число способов подняться на i-ю ступень, -1 означает отсутствие записи
|
||||
mem = [-1] * (n + 1)
|
||||
return dfs(n, mem)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 9
|
||||
|
||||
res = climbing_stairs_dfs_mem(n)
|
||||
print(f"Количество способов подняться по лестнице из {n} ступеней = {res}")
|
||||
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
File: climbing_stairs_dp.py
|
||||
Created Time: 2023-06-30
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def climbing_stairs_dp(n: int) -> int:
|
||||
"""Подъем по лестнице: динамическое программирование"""
|
||||
if n == 1 or n == 2:
|
||||
return n
|
||||
# Инициализация таблицы dp для хранения решений подзадач
|
||||
dp = [0] * (n + 1)
|
||||
# Начальное состояние: заранее задать решения наименьших подзадач
|
||||
dp[1], dp[2] = 1, 2
|
||||
# Переход состояний: постепенное решение больших подзадач через меньшие
|
||||
for i in range(3, n + 1):
|
||||
dp[i] = dp[i - 1] + dp[i - 2]
|
||||
return dp[n]
|
||||
|
||||
|
||||
def climbing_stairs_dp_comp(n: int) -> int:
|
||||
"""Подъем по лестнице: динамическое программирование с оптимизацией памяти"""
|
||||
if n == 1 or n == 2:
|
||||
return n
|
||||
a, b = 1, 2
|
||||
for _ in range(3, n + 1):
|
||||
a, b = b, a + b
|
||||
return b
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 9
|
||||
|
||||
res = climbing_stairs_dp(n)
|
||||
print(f"Количество способов подняться по лестнице из {n} ступеней = {res}")
|
||||
|
||||
res = climbing_stairs_dp_comp(n)
|
||||
print(f"Количество способов подняться по лестнице из {n} ступеней = {res}")
|
||||
60
ru/codes/python/chapter_dynamic_programming/coin_change.py
Normal file
60
ru/codes/python/chapter_dynamic_programming/coin_change.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""
|
||||
File: coin_change.py
|
||||
Created Time: 2023-07-10
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def coin_change_dp(coins: list[int], amt: int) -> int:
|
||||
"""Размен монет: динамическое программирование"""
|
||||
n = len(coins)
|
||||
MAX = amt + 1
|
||||
# Инициализация таблицы dp
|
||||
dp = [[0] * (amt + 1) for _ in range(n + 1)]
|
||||
# Переход состояний: первая строка и первый столбец
|
||||
for a in range(1, amt + 1):
|
||||
dp[0][a] = MAX
|
||||
# Переход состояний: остальные строки и столбцы
|
||||
for i in range(1, n + 1):
|
||||
for a in range(1, amt + 1):
|
||||
if coins[i - 1] > a:
|
||||
# Если целевая сумма превышена, монету i не выбирать
|
||||
dp[i][a] = dp[i - 1][a]
|
||||
else:
|
||||
# Меньшее из двух решений: не брать или взять монету i
|
||||
dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1)
|
||||
return dp[n][amt] if dp[n][amt] != MAX else -1
|
||||
|
||||
|
||||
def coin_change_dp_comp(coins: list[int], amt: int) -> int:
|
||||
"""Размен монет: динамическое программирование с оптимизацией памяти"""
|
||||
n = len(coins)
|
||||
MAX = amt + 1
|
||||
# Инициализация таблицы dp
|
||||
dp = [MAX] * (amt + 1)
|
||||
dp[0] = 0
|
||||
# Переход состояний
|
||||
for i in range(1, n + 1):
|
||||
# Прямой обход
|
||||
for a in range(1, amt + 1):
|
||||
if coins[i - 1] > a:
|
||||
# Если целевая сумма превышена, монету i не выбирать
|
||||
dp[a] = dp[a]
|
||||
else:
|
||||
# Меньшее из двух решений: не брать или взять монету i
|
||||
dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1)
|
||||
return dp[amt] if dp[amt] != MAX else -1
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
coins = [1, 2, 5]
|
||||
amt = 4
|
||||
|
||||
# Динамическое программирование
|
||||
res = coin_change_dp(coins, amt)
|
||||
print(f"Минимальное число монет для набора целевой суммы = {res}")
|
||||
|
||||
# Динамическое программирование с оптимизацией памяти
|
||||
res = coin_change_dp_comp(coins, amt)
|
||||
print(f"Минимальное число монет для набора целевой суммы = {res}")
|
||||
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
File: coin_change_ii.py
|
||||
Created Time: 2023-07-10
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def coin_change_ii_dp(coins: list[int], amt: int) -> int:
|
||||
"""Размен монет II: динамическое программирование"""
|
||||
n = len(coins)
|
||||
# Инициализация таблицы dp
|
||||
dp = [[0] * (amt + 1) for _ in range(n + 1)]
|
||||
# Инициализация первого столбца
|
||||
for i in range(n + 1):
|
||||
dp[i][0] = 1
|
||||
# Переход состояний
|
||||
for i in range(1, n + 1):
|
||||
for a in range(1, amt + 1):
|
||||
if coins[i - 1] > a:
|
||||
# Если целевая сумма превышена, монету i не выбирать
|
||||
dp[i][a] = dp[i - 1][a]
|
||||
else:
|
||||
# Сумма двух решений: не брать или взять монету i
|
||||
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]]
|
||||
return dp[n][amt]
|
||||
|
||||
|
||||
def coin_change_ii_dp_comp(coins: list[int], amt: int) -> int:
|
||||
"""Размен монет II: динамическое программирование с оптимизацией памяти"""
|
||||
n = len(coins)
|
||||
# Инициализация таблицы dp
|
||||
dp = [0] * (amt + 1)
|
||||
dp[0] = 1
|
||||
# Переход состояний
|
||||
for i in range(1, n + 1):
|
||||
# Прямой обход
|
||||
for a in range(1, amt + 1):
|
||||
if coins[i - 1] > a:
|
||||
# Если целевая сумма превышена, монету i не выбирать
|
||||
dp[a] = dp[a]
|
||||
else:
|
||||
# Сумма двух решений: не брать или взять монету i
|
||||
dp[a] = dp[a] + dp[a - coins[i - 1]]
|
||||
return dp[amt]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
coins = [1, 2, 5]
|
||||
amt = 5
|
||||
|
||||
# Динамическое программирование
|
||||
res = coin_change_ii_dp(coins, amt)
|
||||
print(f"Количество комбинаций монет для набора целевой суммы = {res}")
|
||||
|
||||
# Динамическое программирование с оптимизацией памяти
|
||||
res = coin_change_ii_dp_comp(coins, amt)
|
||||
print(f"Количество комбинаций монет для набора целевой суммы = {res}")
|
||||
123
ru/codes/python/chapter_dynamic_programming/edit_distance.py
Normal file
123
ru/codes/python/chapter_dynamic_programming/edit_distance.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""
|
||||
File: edit_distancde.py
|
||||
Created Time: 2023-07-04
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def edit_distance_dfs(s: str, t: str, i: int, j: int) -> int:
|
||||
"""Редакционное расстояние: полный перебор"""
|
||||
# Если s и t пусты, вернуть 0
|
||||
if i == 0 and j == 0:
|
||||
return 0
|
||||
# Если s пусто, вернуть длину t
|
||||
if i == 0:
|
||||
return j
|
||||
# Если t пусто, вернуть длину s
|
||||
if j == 0:
|
||||
return i
|
||||
# Если два символа равны, сразу пропустить их
|
||||
if s[i - 1] == t[j - 1]:
|
||||
return edit_distance_dfs(s, t, i - 1, j - 1)
|
||||
# Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 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 min(insert, delete, replace) + 1
|
||||
|
||||
|
||||
def edit_distance_dfs_mem(s: str, t: str, mem: list[list[int]], i: int, j: int) -> int:
|
||||
"""Редакционное расстояние: поиск с мемоизацией"""
|
||||
# Если s и t пусты, вернуть 0
|
||||
if i == 0 and j == 0:
|
||||
return 0
|
||||
# Если s пусто, вернуть длину t
|
||||
if i == 0:
|
||||
return j
|
||||
# Если t пусто, вернуть длину s
|
||||
if j == 0:
|
||||
return i
|
||||
# Если запись уже есть, сразу вернуть ее
|
||||
if mem[i][j] != -1:
|
||||
return mem[i][j]
|
||||
# Если два символа равны, сразу пропустить их
|
||||
if s[i - 1] == t[j - 1]:
|
||||
return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1)
|
||||
# Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 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)
|
||||
# Сохранить и вернуть минимальное число шагов редактирования
|
||||
mem[i][j] = min(insert, delete, replace) + 1
|
||||
return mem[i][j]
|
||||
|
||||
|
||||
def edit_distance_dp(s: str, t: str) -> int:
|
||||
"""Редакционное расстояние: динамическое программирование"""
|
||||
n, m = len(s), len(t)
|
||||
dp = [[0] * (m + 1) for _ in range(n + 1)]
|
||||
# Переход состояний: первая строка и первый столбец
|
||||
for i in range(1, n + 1):
|
||||
dp[i][0] = i
|
||||
for j in range(1, m + 1):
|
||||
dp[0][j] = j
|
||||
# Переход состояний: остальные строки и столбцы
|
||||
for i in range(1, n + 1):
|
||||
for j in range(1, m + 1):
|
||||
if s[i - 1] == t[j - 1]:
|
||||
# Если два символа равны, сразу пропустить их
|
||||
dp[i][j] = dp[i - 1][j - 1]
|
||||
else:
|
||||
# Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
|
||||
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1
|
||||
return dp[n][m]
|
||||
|
||||
|
||||
def edit_distance_dp_comp(s: str, t: str) -> int:
|
||||
"""Редакционное расстояние: динамическое программирование с оптимизацией памяти"""
|
||||
n, m = len(s), len(t)
|
||||
dp = [0] * (m + 1)
|
||||
# Переход состояний: первая строка
|
||||
for j in range(1, m + 1):
|
||||
dp[j] = j
|
||||
# Переход состояний: остальные строки
|
||||
for i in range(1, n + 1):
|
||||
# Переход состояний: первый столбец
|
||||
leftup = dp[0] # Временно сохранить dp[i-1, j-1]
|
||||
dp[0] += 1
|
||||
# Переход состояний: остальные столбцы
|
||||
for j in range(1, m + 1):
|
||||
temp = dp[j]
|
||||
if s[i - 1] == t[j - 1]:
|
||||
# Если два символа равны, сразу пропустить их
|
||||
dp[j] = leftup
|
||||
else:
|
||||
# Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
|
||||
dp[j] = min(dp[j - 1], dp[j], leftup) + 1
|
||||
leftup = temp # Обновить до значения dp[i-1, j-1] для следующей итерации
|
||||
return dp[m]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
s = "bag"
|
||||
t = "pack"
|
||||
n, m = len(s), len(t)
|
||||
|
||||
# Полный перебор
|
||||
res = edit_distance_dfs(s, t, n, m)
|
||||
print(f"Чтобы преобразовать {s} в {t}, нужно минимум {res} шагов")
|
||||
|
||||
# Поиск с мемоизацией
|
||||
mem = [[-1] * (m + 1) for _ in range(n + 1)]
|
||||
res = edit_distance_dfs_mem(s, t, mem, n, m)
|
||||
print(f"Чтобы преобразовать {s} в {t}, нужно минимум {res} шагов")
|
||||
|
||||
# Динамическое программирование
|
||||
res = edit_distance_dp(s, t)
|
||||
print(f"Чтобы преобразовать {s} в {t}, нужно минимум {res} шагов")
|
||||
|
||||
# Динамическое программирование с оптимизацией памяти
|
||||
res = edit_distance_dp_comp(s, t)
|
||||
print(f"Чтобы преобразовать {s} в {t}, нужно минимум {res} шагов")
|
||||
101
ru/codes/python/chapter_dynamic_programming/knapsack.py
Normal file
101
ru/codes/python/chapter_dynamic_programming/knapsack.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
File: knapsack.py
|
||||
Created Time: 2023-07-03
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def knapsack_dfs(wgt: list[int], val: list[int], i: int, c: int) -> int:
|
||||
"""Рюкзак 0-1: полный перебор"""
|
||||
# Если все предметы уже рассмотрены или в рюкзаке не осталось места, вернуть стоимость 0
|
||||
if i == 0 or c == 0:
|
||||
return 0
|
||||
# Если вместимость рюкзака превышена, можно только не класть предмет в рюкзак
|
||||
if wgt[i - 1] > c:
|
||||
return knapsack_dfs(wgt, val, i - 1, c)
|
||||
# Вычислить максимальную стоимость для случаев, когда предмет i не кладут и кладут
|
||||
no = knapsack_dfs(wgt, val, i - 1, c)
|
||||
yes = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]
|
||||
# Вернуть вариант с большей стоимостью из двух возможных
|
||||
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: поиск с мемоизацией"""
|
||||
# Если все предметы уже рассмотрены или в рюкзаке не осталось места, вернуть стоимость 0
|
||||
if i == 0 or c == 0:
|
||||
return 0
|
||||
# Если запись уже есть, вернуть сразу
|
||||
if mem[i][c] != -1:
|
||||
return mem[i][c]
|
||||
# Если вместимость рюкзака превышена, можно только не класть предмет в рюкзак
|
||||
if wgt[i - 1] > c:
|
||||
return knapsack_dfs_mem(wgt, val, mem, i - 1, c)
|
||||
# Вычислить максимальную стоимость для случаев, когда предмет 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]
|
||||
# Сохранить и вернуть вариант с большей стоимостью из двух решений
|
||||
mem[i][c] = max(no, yes)
|
||||
return mem[i][c]
|
||||
|
||||
|
||||
def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int:
|
||||
"""Рюкзак 0-1: динамическое программирование"""
|
||||
n = len(wgt)
|
||||
# Инициализация таблицы dp
|
||||
dp = [[0] * (cap + 1) for _ in range(n + 1)]
|
||||
# Переход состояний
|
||||
for i in range(1, n + 1):
|
||||
for c in range(1, cap + 1):
|
||||
if wgt[i - 1] > c:
|
||||
# Если вместимость рюкзака превышена, предмет i не выбирать
|
||||
dp[i][c] = dp[i - 1][c]
|
||||
else:
|
||||
# Большее из двух решений: не брать или взять предмет 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: динамическое программирование с оптимизацией памяти"""
|
||||
n = len(wgt)
|
||||
# Инициализация таблицы dp
|
||||
dp = [0] * (cap + 1)
|
||||
# Переход состояний
|
||||
for i in range(1, n + 1):
|
||||
# Обход в обратном порядке
|
||||
for c in range(cap, 0, -1):
|
||||
if wgt[i - 1] > c:
|
||||
# Если вместимость рюкзака превышена, предмет i не выбирать
|
||||
dp[c] = dp[c]
|
||||
else:
|
||||
# Большее из двух решений: не брать или взять предмет i
|
||||
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1])
|
||||
return dp[cap]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
wgt = [10, 20, 30, 40, 50]
|
||||
val = [50, 120, 150, 210, 240]
|
||||
cap = 50
|
||||
n = len(wgt)
|
||||
|
||||
# Полный перебор
|
||||
res = knapsack_dfs(wgt, val, n, cap)
|
||||
print(f"Максимальная стоимость предметов без превышения вместимости рюкзака = {res}")
|
||||
|
||||
# Поиск с мемоизацией
|
||||
mem = [[-1] * (cap + 1) for _ in range(n + 1)]
|
||||
res = knapsack_dfs_mem(wgt, val, mem, n, cap)
|
||||
print(f"Максимальная стоимость предметов без превышения вместимости рюкзака = {res}")
|
||||
|
||||
# Динамическое программирование
|
||||
res = knapsack_dp(wgt, val, cap)
|
||||
print(f"Максимальная стоимость предметов без превышения вместимости рюкзака = {res}")
|
||||
|
||||
# Динамическое программирование с оптимизацией памяти
|
||||
res = knapsack_dp_comp(wgt, val, cap)
|
||||
print(f"Максимальная стоимость предметов без превышения вместимости рюкзака = {res}")
|
||||
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
File: min_cost_climbing_stairs_dp.py
|
||||
Created Time: 2023-06-30
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def min_cost_climbing_stairs_dp(cost: list[int]) -> int:
|
||||
"""Минимальная стоимость подъема по лестнице: динамическое программирование"""
|
||||
n = len(cost) - 1
|
||||
if n == 1 or n == 2:
|
||||
return cost[n]
|
||||
# Инициализация таблицы dp для хранения решений подзадач
|
||||
dp = [0] * (n + 1)
|
||||
# Начальное состояние: заранее задать решения наименьших подзадач
|
||||
dp[1], dp[2] = cost[1], cost[2]
|
||||
# Переход состояний: постепенное решение больших подзадач через меньшие
|
||||
for i in range(3, n + 1):
|
||||
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
|
||||
return dp[n]
|
||||
|
||||
|
||||
def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int:
|
||||
"""Минимальная стоимость подъема по лестнице: динамическое программирование с оптимизацией памяти"""
|
||||
n = len(cost) - 1
|
||||
if n == 1 or n == 2:
|
||||
return cost[n]
|
||||
a, b = cost[1], cost[2]
|
||||
for i in range(3, n + 1):
|
||||
a, b = b, min(a, b) + cost[i]
|
||||
return b
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]
|
||||
print(f"Список стоимостей ступеней = {cost}")
|
||||
|
||||
res = min_cost_climbing_stairs_dp(cost)
|
||||
print(f"Минимальная стоимость подъема по лестнице = {res}")
|
||||
|
||||
res = min_cost_climbing_stairs_dp_comp(cost)
|
||||
print(f"Минимальная стоимость подъема по лестнице = {res}")
|
||||
104
ru/codes/python/chapter_dynamic_programming/min_path_sum.py
Normal file
104
ru/codes/python/chapter_dynamic_programming/min_path_sum.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
File: min_path_sum.py
|
||||
Created Time: 2023-07-04
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
from math import inf
|
||||
|
||||
|
||||
def min_path_sum_dfs(grid: list[list[int]], i: int, j: int) -> int:
|
||||
"""Минимальная сумма пути: полный перебор"""
|
||||
# Если это верхняя левая ячейка, завершить поиск
|
||||
if i == 0 and j == 0:
|
||||
return grid[0][0]
|
||||
# Если индексы строки или столбца выходят за границы, вернуть стоимость +∞
|
||||
if i < 0 or j < 0:
|
||||
return inf
|
||||
# Вычислить минимальную стоимость пути из левого верхнего угла до (i-1, j) и (i, j-1)
|
||||
up = min_path_sum_dfs(grid, i - 1, j)
|
||||
left = min_path_sum_dfs(grid, i, j - 1)
|
||||
# Вернуть минимальную стоимость пути из левого верхнего угла до (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:
|
||||
"""Минимальная сумма пути: поиск с мемоизацией"""
|
||||
# Если это верхняя левая ячейка, завершить поиск
|
||||
if i == 0 and j == 0:
|
||||
return grid[0][0]
|
||||
# Если индексы строки или столбца выходят за границы, вернуть стоимость +∞
|
||||
if i < 0 or j < 0:
|
||||
return inf
|
||||
# Если запись уже есть, вернуть сразу
|
||||
if mem[i][j] != -1:
|
||||
return mem[i][j]
|
||||
# Минимальная стоимость пути для левой и верхней ячеек
|
||||
up = min_path_sum_dfs_mem(grid, mem, i - 1, j)
|
||||
left = min_path_sum_dfs_mem(grid, mem, i, j - 1)
|
||||
# Сохранить и вернуть минимальную стоимость пути из левого верхнего угла до (i, j)
|
||||
mem[i][j] = min(left, up) + grid[i][j]
|
||||
return mem[i][j]
|
||||
|
||||
|
||||
def min_path_sum_dp(grid: list[list[int]]) -> int:
|
||||
"""Минимальная сумма пути: динамическое программирование"""
|
||||
n, m = len(grid), len(grid[0])
|
||||
# Инициализация таблицы dp
|
||||
dp = [[0] * m for _ in range(n)]
|
||||
dp[0][0] = grid[0][0]
|
||||
# Переход состояний: первая строка
|
||||
for j in range(1, m):
|
||||
dp[0][j] = dp[0][j - 1] + grid[0][j]
|
||||
# Переход состояний: первый столбец
|
||||
for i in range(1, n):
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0]
|
||||
# Переход состояний: остальные строки и столбцы
|
||||
for i in range(1, n):
|
||||
for j in range(1, m):
|
||||
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
|
||||
return dp[n - 1][m - 1]
|
||||
|
||||
|
||||
def min_path_sum_dp_comp(grid: list[list[int]]) -> int:
|
||||
"""Минимальная сумма пути: динамическое программирование с оптимизацией памяти"""
|
||||
n, m = len(grid), len(grid[0])
|
||||
# Инициализация таблицы dp
|
||||
dp = [0] * m
|
||||
# Переход состояний: первая строка
|
||||
dp[0] = grid[0][0]
|
||||
for j in range(1, m):
|
||||
dp[j] = dp[j - 1] + grid[0][j]
|
||||
# Переход состояний: остальные строки
|
||||
for i in range(1, n):
|
||||
# Переход состояний: первый столбец
|
||||
dp[0] = dp[0] + grid[i][0]
|
||||
# Переход состояний: остальные столбцы
|
||||
for j in range(1, m):
|
||||
dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]
|
||||
return dp[m - 1]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
grid = [[1, 3, 1, 5], [2, 2, 4, 2], [5, 3, 2, 1], [4, 3, 5, 2]]
|
||||
n, m = len(grid), len(grid[0])
|
||||
|
||||
# Полный перебор
|
||||
res = min_path_sum_dfs(grid, n - 1, m - 1)
|
||||
print(f"Минимальная сумма пути из левого верхнего угла в правый нижний = {res}")
|
||||
|
||||
# Поиск с мемоизацией
|
||||
mem = [[-1] * m for _ in range(n)]
|
||||
res = min_path_sum_dfs_mem(grid, mem, n - 1, m - 1)
|
||||
print(f"Минимальная сумма пути из левого верхнего угла в правый нижний = {res}")
|
||||
|
||||
# Динамическое программирование
|
||||
res = min_path_sum_dp(grid)
|
||||
print(f"Минимальная сумма пути из левого верхнего угла в правый нижний = {res}")
|
||||
|
||||
# Динамическое программирование с оптимизацией памяти
|
||||
res = min_path_sum_dp_comp(grid)
|
||||
print(f"Минимальная сумма пути из левого верхнего угла в правый нижний = {res}")
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
File: unbounded_knapsack.py
|
||||
Created Time: 2023-07-10
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int:
|
||||
"""Полный рюкзак: динамическое программирование"""
|
||||
n = len(wgt)
|
||||
# Инициализация таблицы dp
|
||||
dp = [[0] * (cap + 1) for _ in range(n + 1)]
|
||||
# Переход состояний
|
||||
for i in range(1, n + 1):
|
||||
for c in range(1, cap + 1):
|
||||
if wgt[i - 1] > c:
|
||||
# Если вместимость рюкзака превышена, предмет i не выбирать
|
||||
dp[i][c] = dp[i - 1][c]
|
||||
else:
|
||||
# Большее из двух решений: не брать или взять предмет 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:
|
||||
"""Полный рюкзак: динамическое программирование с оптимизацией памяти"""
|
||||
n = len(wgt)
|
||||
# Инициализация таблицы dp
|
||||
dp = [0] * (cap + 1)
|
||||
# Переход состояний
|
||||
for i in range(1, n + 1):
|
||||
# Прямой обход
|
||||
for c in range(1, cap + 1):
|
||||
if wgt[i - 1] > c:
|
||||
# Если вместимость рюкзака превышена, предмет i не выбирать
|
||||
dp[c] = dp[c]
|
||||
else:
|
||||
# Большее из двух решений: не брать или взять предмет i
|
||||
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1])
|
||||
return dp[cap]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
wgt = [1, 2, 3]
|
||||
val = [5, 11, 15]
|
||||
cap = 4
|
||||
|
||||
# Динамическое программирование
|
||||
res = unbounded_knapsack_dp(wgt, val, cap)
|
||||
print(f"Максимальная стоимость предметов без превышения вместимости рюкзака = {res}")
|
||||
|
||||
# Динамическое программирование с оптимизацией памяти
|
||||
res = unbounded_knapsack_dp_comp(wgt, val, cap)
|
||||
print(f"Максимальная стоимость предметов без превышения вместимости рюкзака = {res}")
|
||||
111
ru/codes/python/chapter_graph/graph_adjacency_list.py
Normal file
111
ru/codes/python/chapter_graph/graph_adjacency_list.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
File: graph_adjacency_list.py
|
||||
Created Time: 2023-02-23
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import Vertex, vals_to_vets
|
||||
|
||||
|
||||
class GraphAdjList:
|
||||
"""Класс неориентированного графа на основе списка смежности"""
|
||||
|
||||
def __init__(self, edges: list[list[Vertex]]):
|
||||
"""Конструктор"""
|
||||
# Список смежности, где key — вершина, а value — все смежные ей вершины
|
||||
self.adj_list = dict[Vertex, list[Vertex]]()
|
||||
# Добавить все вершины и ребра
|
||||
for edge in edges:
|
||||
self.add_vertex(edge[0])
|
||||
self.add_vertex(edge[1])
|
||||
self.add_edge(edge[0], edge[1])
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получить число вершин"""
|
||||
return len(self.adj_list)
|
||||
|
||||
def add_edge(self, vet1: Vertex, vet2: Vertex):
|
||||
"""Добавление ребра"""
|
||||
if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2:
|
||||
raise ValueError()
|
||||
# Добавить ребро vet1 - vet2
|
||||
self.adj_list[vet1].append(vet2)
|
||||
self.adj_list[vet2].append(vet1)
|
||||
|
||||
def remove_edge(self, vet1: Vertex, vet2: Vertex):
|
||||
"""Удаление ребра"""
|
||||
if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2:
|
||||
raise ValueError()
|
||||
# Удалить ребро vet1 - vet2
|
||||
self.adj_list[vet1].remove(vet2)
|
||||
self.adj_list[vet2].remove(vet1)
|
||||
|
||||
def add_vertex(self, vet: Vertex):
|
||||
"""Добавление вершины"""
|
||||
if vet in self.adj_list:
|
||||
return
|
||||
# Добавить новый список в список смежности
|
||||
self.adj_list[vet] = []
|
||||
|
||||
def remove_vertex(self, vet: Vertex):
|
||||
"""Удаление вершины"""
|
||||
if vet not in self.adj_list:
|
||||
raise ValueError()
|
||||
# Удалить из списка смежности список, соответствующий вершине vet
|
||||
self.adj_list.pop(vet)
|
||||
# Обойти списки других вершин и удалить все ребра, содержащие vet
|
||||
for vertex in self.adj_list:
|
||||
if vet in self.adj_list[vertex]:
|
||||
self.adj_list[vertex].remove(vet)
|
||||
|
||||
def print(self):
|
||||
"""Вывести список смежности"""
|
||||
print("Список смежности =")
|
||||
for vertex in self.adj_list:
|
||||
tmp = [v.val for v in self.adj_list[vertex]]
|
||||
print(f"{vertex.val}: {tmp},")
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация неориентированного графа
|
||||
v = vals_to_vets([1, 3, 2, 5, 4])
|
||||
edges = [
|
||||
[v[0], v[1]],
|
||||
[v[0], v[3]],
|
||||
[v[1], v[2]],
|
||||
[v[2], v[3]],
|
||||
[v[2], v[4]],
|
||||
[v[3], v[4]],
|
||||
]
|
||||
graph = GraphAdjList(edges)
|
||||
print("\nГраф после инициализации")
|
||||
graph.print()
|
||||
|
||||
# Добавить ребро
|
||||
# Вершины 1 и 2, то есть v[0] и v[2]
|
||||
graph.add_edge(v[0], v[2])
|
||||
print("\nГраф после добавления ребра 1-2")
|
||||
graph.print()
|
||||
|
||||
# Удалить ребро
|
||||
# Вершины 1 и 3 соответствуют v[0] и v[1]
|
||||
graph.remove_edge(v[0], v[1])
|
||||
print("\nГраф после удаления ребра 1-3")
|
||||
graph.print()
|
||||
|
||||
# Добавление вершины
|
||||
v5 = Vertex(6)
|
||||
graph.add_vertex(v5)
|
||||
print("\nГраф после добавления вершины 6")
|
||||
graph.print()
|
||||
|
||||
# Удаление вершины
|
||||
# Вершина 3 соответствует v[1]
|
||||
graph.remove_vertex(v[1])
|
||||
print("\nГраф после удаления вершины 3")
|
||||
graph.print()
|
||||
116
ru/codes/python/chapter_graph/graph_adjacency_matrix.py
Normal file
116
ru/codes/python/chapter_graph/graph_adjacency_matrix.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
File: graph_adjacency_matrix.py
|
||||
Created Time: 2023-02-23
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import Vertex, print_matrix
|
||||
|
||||
|
||||
class GraphAdjMat:
|
||||
"""Класс неориентированного графа на основе матрицы смежности"""
|
||||
|
||||
def __init__(self, vertices: list[int], edges: list[list[int]]):
|
||||
"""Конструктор"""
|
||||
# Список вершин: элементы представляют «значения вершин», а индексы — «индексы вершин»
|
||||
self.vertices: list[int] = []
|
||||
# Матрица смежности, где индексы строк и столбцов соответствуют «индексам вершин»
|
||||
self.adj_mat: list[list[int]] = []
|
||||
# Добавление вершины
|
||||
for val in vertices:
|
||||
self.add_vertex(val)
|
||||
# Добавить ребра
|
||||
# Обратите внимание: элементы edges представляют собой индексы вершин, то есть соответствуют индексам элементов vertices
|
||||
for e in edges:
|
||||
self.add_edge(e[0], e[1])
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получить число вершин"""
|
||||
return len(self.vertices)
|
||||
|
||||
def add_vertex(self, val: int):
|
||||
"""Добавление вершины"""
|
||||
n = self.size()
|
||||
# Добавить значение новой вершины в список вершин
|
||||
self.vertices.append(val)
|
||||
# Добавить строку в матрицу смежности
|
||||
new_row = [0] * n
|
||||
self.adj_mat.append(new_row)
|
||||
# Добавить столбец в матрицу смежности
|
||||
for row in self.adj_mat:
|
||||
row.append(0)
|
||||
|
||||
def remove_vertex(self, index: int):
|
||||
"""Удаление вершины"""
|
||||
if index >= self.size():
|
||||
raise IndexError()
|
||||
# Удалить вершину с индексом index из списка вершин
|
||||
self.vertices.pop(index)
|
||||
# Удалить строку с индексом index из матрицы смежности
|
||||
self.adj_mat.pop(index)
|
||||
# Удалить столбец с индексом index из матрицы смежности
|
||||
for row in self.adj_mat:
|
||||
row.pop(index)
|
||||
|
||||
def add_edge(self, i: int, j: int):
|
||||
"""Добавление ребра"""
|
||||
# Параметры i и j соответствуют индексам элементов vertices
|
||||
# Обработка выхода индекса за границы и случая равенства
|
||||
if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j:
|
||||
raise IndexError()
|
||||
# В неориентированном графе матрица смежности симметрична относительно главной диагонали, то есть выполняется (i, j) == (j, i)
|
||||
self.adj_mat[i][j] = 1
|
||||
self.adj_mat[j][i] = 1
|
||||
|
||||
def remove_edge(self, i: int, j: int):
|
||||
"""Удаление ребра"""
|
||||
# Параметры i и j соответствуют индексам элементов vertices
|
||||
# Обработка выхода индекса за границы и случая равенства
|
||||
if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j:
|
||||
raise IndexError()
|
||||
self.adj_mat[i][j] = 0
|
||||
self.adj_mat[j][i] = 0
|
||||
|
||||
def print(self):
|
||||
"""Вывести матрицу смежности"""
|
||||
print("Список вершин =", self.vertices)
|
||||
print("Матрица смежности =")
|
||||
print_matrix(self.adj_mat)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация неориентированного графа
|
||||
# Обратите внимание: элементы edges представляют индексы вершин, то есть соответствуют индексам элементов vertices
|
||||
vertices = [1, 3, 2, 5, 4]
|
||||
edges = [[0, 1], [0, 3], [1, 2], [2, 3], [2, 4], [3, 4]]
|
||||
graph = GraphAdjMat(vertices, edges)
|
||||
print("\nГраф после инициализации")
|
||||
graph.print()
|
||||
|
||||
# Добавление ребра
|
||||
# Индексы вершин 1 и 2 равны 0 и 2 соответственно
|
||||
graph.add_edge(0, 2)
|
||||
print("\nГраф после добавления ребра 1-2")
|
||||
graph.print()
|
||||
|
||||
# Удалить ребро
|
||||
# Индексы вершин 1 и 3 равны 0 и 1
|
||||
graph.remove_edge(0, 1)
|
||||
print("\nГраф после удаления ребра 1-3")
|
||||
graph.print()
|
||||
|
||||
# Добавление вершины
|
||||
graph.add_vertex(6)
|
||||
print("\nГраф после добавления вершины 6")
|
||||
graph.print()
|
||||
|
||||
# Удаление вершины
|
||||
# Индекс вершины 3 равен 1
|
||||
graph.remove_vertex(1)
|
||||
print("\nГраф после удаления вершины 3")
|
||||
graph.print()
|
||||
64
ru/codes/python/chapter_graph/graph_bfs.py
Normal file
64
ru/codes/python/chapter_graph/graph_bfs.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""
|
||||
File: graph_bfs.py
|
||||
Created Time: 2023-02-23
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import Vertex, vals_to_vets, vets_to_vals
|
||||
from collections import deque
|
||||
from graph_adjacency_list import GraphAdjList
|
||||
|
||||
|
||||
def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
|
||||
"""Обход в ширину"""
|
||||
# Использовать список смежности для представления графа, чтобы получать все смежные вершины заданной вершины
|
||||
# Последовательность обхода вершин
|
||||
res = []
|
||||
# Хеш-множество для хранения уже посещенных вершин
|
||||
visited = set[Vertex]([start_vet])
|
||||
# Очередь используется для реализации BFS
|
||||
que = deque[Vertex]([start_vet])
|
||||
# Начиная с вершины vet, продолжать цикл, пока не будут посещены все вершины
|
||||
while len(que) > 0:
|
||||
vet = que.popleft() # Извлечь головную вершину из очереди
|
||||
res.append(vet) # Отметить посещенную вершину
|
||||
# Обойти все смежные вершины данной вершины
|
||||
for adj_vet in graph.adj_list[vet]:
|
||||
if adj_vet in visited:
|
||||
continue # Пропустить уже посещенную вершину
|
||||
que.append(adj_vet) # Помещать в очередь только непосещенные вершины
|
||||
visited.add(adj_vet) # Отметить эту вершину как посещенную
|
||||
# Вернуть последовательность обхода вершин
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация неориентированного графа
|
||||
v = vals_to_vets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
edges = [
|
||||
[v[0], v[1]],
|
||||
[v[0], v[3]],
|
||||
[v[1], v[2]],
|
||||
[v[1], v[4]],
|
||||
[v[2], v[5]],
|
||||
[v[3], v[4]],
|
||||
[v[3], v[6]],
|
||||
[v[4], v[5]],
|
||||
[v[4], v[7]],
|
||||
[v[5], v[8]],
|
||||
[v[6], v[7]],
|
||||
[v[7], v[8]],
|
||||
]
|
||||
graph = GraphAdjList(edges)
|
||||
print("\nГраф после инициализации")
|
||||
graph.print()
|
||||
|
||||
# Обход в ширину
|
||||
res = graph_bfs(graph, v[0])
|
||||
print("\nПоследовательность вершин при обходе в ширину (BFS)")
|
||||
print(vets_to_vals(res))
|
||||
57
ru/codes/python/chapter_graph/graph_dfs.py
Normal file
57
ru/codes/python/chapter_graph/graph_dfs.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
File: graph_dfs.py
|
||||
Created Time: 2023-02-23
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import Vertex, vets_to_vals, vals_to_vets
|
||||
from graph_adjacency_list import GraphAdjList
|
||||
|
||||
|
||||
def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Vertex):
|
||||
"""Вспомогательная функция обхода в глубину"""
|
||||
res.append(vet) # Отметить посещенную вершину
|
||||
visited.add(vet) # Отметить эту вершину как посещенную
|
||||
# Обойти все смежные вершины данной вершины
|
||||
for adjVet in graph.adj_list[vet]:
|
||||
if adjVet in visited:
|
||||
continue # Пропустить уже посещенную вершину
|
||||
# Рекурсивно обходить смежные вершины
|
||||
dfs(graph, visited, res, adjVet)
|
||||
|
||||
|
||||
def graph_dfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
|
||||
"""Обход в глубину"""
|
||||
# Использовать список смежности для представления графа, чтобы получать все смежные вершины заданной вершины
|
||||
# Последовательность обхода вершин
|
||||
res = []
|
||||
# Хеш-множество для хранения уже посещенных вершин
|
||||
visited = set[Vertex]()
|
||||
dfs(graph, visited, res, start_vet)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация неориентированного графа
|
||||
v = vals_to_vets([0, 1, 2, 3, 4, 5, 6])
|
||||
edges = [
|
||||
[v[0], v[1]],
|
||||
[v[0], v[3]],
|
||||
[v[1], v[2]],
|
||||
[v[2], v[5]],
|
||||
[v[4], v[5]],
|
||||
[v[5], v[6]],
|
||||
]
|
||||
graph = GraphAdjList(edges)
|
||||
print("\nГраф после инициализации")
|
||||
graph.print()
|
||||
|
||||
# Обход в глубину
|
||||
res = graph_dfs(graph, v[0])
|
||||
print("\nПоследовательность вершин при обходе в глубину (DFS)")
|
||||
print(vets_to_vals(res))
|
||||
48
ru/codes/python/chapter_greedy/coin_change_greedy.py
Normal file
48
ru/codes/python/chapter_greedy/coin_change_greedy.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""
|
||||
File: coin_change_greedy.py
|
||||
Created Time: 2023-07-18
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def coin_change_greedy(coins: list[int], amt: int) -> int:
|
||||
"""Размен монет: жадный алгоритм"""
|
||||
# Предположить, что список coins упорядочен
|
||||
i = len(coins) - 1
|
||||
count = 0
|
||||
# Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while amt > 0:
|
||||
# Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while i > 0 and coins[i] > amt:
|
||||
i -= 1
|
||||
# Выбрать coins[i]
|
||||
amt -= coins[i]
|
||||
count += 1
|
||||
# Если допустимое решение не найдено, вернуть -1
|
||||
return count if amt == 0 else -1
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Жадный подход: гарантирует нахождение глобально оптимального решения
|
||||
coins = [1, 5, 10, 20, 50, 100]
|
||||
amt = 186
|
||||
res = coin_change_greedy(coins, amt)
|
||||
print(f"\ncoins = {coins}, amt = {amt}")
|
||||
print(f"Минимальное число монет для набора суммы {amt} = {res}")
|
||||
|
||||
# Жадный подход: не гарантирует нахождение глобально оптимального решения
|
||||
coins = [1, 20, 50]
|
||||
amt = 60
|
||||
res = coin_change_greedy(coins, amt)
|
||||
print(f"\ncoins = {coins}, amt = {amt}")
|
||||
print(f"Минимальное число монет для набора суммы {amt} = {res}")
|
||||
print(f"На самом деле минимум равен 3: 20 + 20 + 20")
|
||||
|
||||
# Жадный подход: не гарантирует нахождение глобально оптимального решения
|
||||
coins = [1, 49, 50]
|
||||
amt = 98
|
||||
res = coin_change_greedy(coins, amt)
|
||||
print(f"\ncoins = {coins}, amt = {amt}")
|
||||
print(f"Минимальное число монет для набора суммы {amt} = {res}")
|
||||
print(f"На самом деле минимум равен 2: 49 + 49")
|
||||
46
ru/codes/python/chapter_greedy/fractional_knapsack.py
Normal file
46
ru/codes/python/chapter_greedy/fractional_knapsack.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""
|
||||
File: fractional_knapsack.py
|
||||
Created Time: 2023-07-19
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
class Item:
|
||||
"""Предмет"""
|
||||
|
||||
def __init__(self, w: int, v: int):
|
||||
self.w = w # Вес предмета
|
||||
self.v = v # Стоимость предмета
|
||||
|
||||
|
||||
def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int:
|
||||
"""Дробный рюкзак: жадный алгоритм"""
|
||||
# Создать список предметов с двумя свойствами: вес и стоимость
|
||||
items = [Item(w, v) for w, v in zip(wgt, val)]
|
||||
# Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sort(key=lambda item: item.v / item.w, reverse=True)
|
||||
# Циклический жадный выбор
|
||||
res = 0
|
||||
for item in items:
|
||||
if item.w <= cap:
|
||||
# Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v
|
||||
cap -= item.w
|
||||
else:
|
||||
# Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (item.v / item.w) * cap
|
||||
# Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
wgt = [10, 20, 30, 40, 50]
|
||||
val = [50, 120, 150, 210, 240]
|
||||
cap = 50
|
||||
n = len(wgt)
|
||||
|
||||
# Жадный алгоритм
|
||||
res = fractional_knapsack(wgt, val, cap)
|
||||
print(f"Максимальная стоимость предметов без превышения вместимости рюкзака = {res}")
|
||||
33
ru/codes/python/chapter_greedy/max_capacity.py
Normal file
33
ru/codes/python/chapter_greedy/max_capacity.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
File: max_capacity.py
|
||||
Created Time: 2023-07-21
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def max_capacity(ht: list[int]) -> int:
|
||||
"""Максимальная вместимость: жадный алгоритм"""
|
||||
# Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
i, j = 0, len(ht) - 1
|
||||
# Начальная максимальная вместимость равна 0
|
||||
res = 0
|
||||
# Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while i < j:
|
||||
# Обновить максимальную вместимость
|
||||
cap = min(ht[i], ht[j]) * (j - i)
|
||||
res = max(res, cap)
|
||||
# Сдвигать внутрь более короткую сторону
|
||||
if ht[i] < ht[j]:
|
||||
i += 1
|
||||
else:
|
||||
j -= 1
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
ht = [3, 8, 5, 2, 7, 7, 3, 4]
|
||||
|
||||
# Жадный алгоритм
|
||||
res = max_capacity(ht)
|
||||
print(f"Максимальная вместимость = {res}")
|
||||
33
ru/codes/python/chapter_greedy/max_product_cutting.py
Normal file
33
ru/codes/python/chapter_greedy/max_product_cutting.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
File: max_product_cutting.py
|
||||
Created Time: 2023-07-21
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
|
||||
def max_product_cutting(n: int) -> int:
|
||||
"""Максимальное произведение разрезания: жадный алгоритм"""
|
||||
# Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if n <= 3:
|
||||
return 1 * (n - 1)
|
||||
# Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
a, b = n // 3, n % 3
|
||||
if b == 1:
|
||||
# Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return int(math.pow(3, a - 1)) * 2 * 2
|
||||
if b == 2:
|
||||
# Если остаток равен 2, ничего не делать
|
||||
return int(math.pow(3, a)) * 2
|
||||
# Если остаток равен 0, ничего не делать
|
||||
return int(math.pow(3, a))
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
n = 58
|
||||
|
||||
# Жадный алгоритм
|
||||
res = max_product_cutting(n)
|
||||
print(f"Максимальное произведение после разрезания = {res}")
|
||||
117
ru/codes/python/chapter_hashing/array_hash_map.py
Normal file
117
ru/codes/python/chapter_hashing/array_hash_map.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""
|
||||
File: array_hash_map.py
|
||||
Created Time: 2022-12-14
|
||||
Author: msk397 (machangxinq@gmail.com)
|
||||
"""
|
||||
|
||||
|
||||
class Pair:
|
||||
"""Пара ключ-значение"""
|
||||
|
||||
def __init__(self, key: int, val: str):
|
||||
self.key = key
|
||||
self.val = val
|
||||
|
||||
|
||||
class ArrayHashMap:
|
||||
"""Хеш-таблица на основе массива"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
# Инициализировать массив, содержащий 100 корзин
|
||||
self.buckets: list[Pair | None] = [None] * 100
|
||||
|
||||
def hash_func(self, key: int) -> int:
|
||||
"""Хеш-функция"""
|
||||
index = key % 100
|
||||
return index
|
||||
|
||||
def get(self, key: int) -> str | None:
|
||||
"""Операция поиска"""
|
||||
index: int = self.hash_func(key)
|
||||
pair: Pair = self.buckets[index]
|
||||
if pair is None:
|
||||
return None
|
||||
return pair.val
|
||||
|
||||
def put(self, key: int, val: str):
|
||||
"""Операции добавления и обновления"""
|
||||
pair = Pair(key, val)
|
||||
index: int = self.hash_func(key)
|
||||
self.buckets[index] = pair
|
||||
|
||||
def remove(self, key: int):
|
||||
"""Операция удаления"""
|
||||
index: int = self.hash_func(key)
|
||||
# Присвоить None, что означает удаление
|
||||
self.buckets[index] = None
|
||||
|
||||
def entry_set(self) -> list[Pair]:
|
||||
"""Получить все пары ключ-значение"""
|
||||
result: list[Pair] = []
|
||||
for pair in self.buckets:
|
||||
if pair is not None:
|
||||
result.append(pair)
|
||||
return result
|
||||
|
||||
def key_set(self) -> list[int]:
|
||||
"""Получить все ключи"""
|
||||
result = []
|
||||
for pair in self.buckets:
|
||||
if pair is not None:
|
||||
result.append(pair.key)
|
||||
return result
|
||||
|
||||
def value_set(self) -> list[str]:
|
||||
"""Получить все значения"""
|
||||
result = []
|
||||
for pair in self.buckets:
|
||||
if pair is not None:
|
||||
result.append(pair.val)
|
||||
return result
|
||||
|
||||
def print(self):
|
||||
"""Вывести хеш-таблицу"""
|
||||
for pair in self.buckets:
|
||||
if pair is not None:
|
||||
print(pair.key, "->", pair.val)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация хеш-таблицы
|
||||
hmap = ArrayHashMap()
|
||||
|
||||
# Операция добавления
|
||||
# Добавить пару (key, value) в хеш-таблицу
|
||||
hmap.put(12836, "Сяо Ха")
|
||||
hmap.put(15937, "Сяо Ло")
|
||||
hmap.put(16750, "Сяо Суань")
|
||||
hmap.put(13276, "Сяо Фа")
|
||||
hmap.put(10583, "Сяо Я")
|
||||
print("\nПосле добавления хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
hmap.print()
|
||||
|
||||
# Операция поиска
|
||||
# Передать ключ key в хеш-таблицу и получить значение value
|
||||
name = hmap.get(15937)
|
||||
print("\nДля номера 15937 найдено имя " + name)
|
||||
|
||||
# Операция удаления
|
||||
# Удалить пару (key, value) из хеш-таблицы
|
||||
hmap.remove(10583)
|
||||
print("\nПосле удаления 10583 хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
hmap.print()
|
||||
|
||||
# Обход хеш-таблицы
|
||||
print("\nОтдельный обход пар ключ-значение")
|
||||
for pair in hmap.entry_set():
|
||||
print(pair.key, "->", pair.val)
|
||||
|
||||
print("\nОтдельный обход ключей")
|
||||
for key in hmap.key_set():
|
||||
print(key)
|
||||
|
||||
print("\nОтдельный обход значений")
|
||||
for val in hmap.value_set():
|
||||
print(val)
|
||||
37
ru/codes/python/chapter_hashing/built_in_hash.py
Normal file
37
ru/codes/python/chapter_hashing/built_in_hash.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
File: built_in_hash.py
|
||||
Created Time: 2023-06-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import ListNode
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
num = 3
|
||||
hash_num = hash(num)
|
||||
print(f"Хеш-значение целого числа {num} = {hash_num}")
|
||||
|
||||
bol = True
|
||||
hash_bol = hash(bol)
|
||||
print(f"Хеш-значение булева значения {bol} = {hash_bol}")
|
||||
|
||||
dec = 3.14159
|
||||
hash_dec = hash(dec)
|
||||
print(f"Хеш-значение десятичного числа {dec} = {hash_dec}")
|
||||
|
||||
str = "Hello Algo"
|
||||
hash_str = hash(str)
|
||||
print(f"Хеш-значение строки {str} = {hash_str}")
|
||||
|
||||
tup = (12836, "Сяо Ха")
|
||||
hash_tup = hash(tup)
|
||||
print(f"Хеш-значение кортежа {tup} = {hash(hash_tup)}")
|
||||
|
||||
obj = ListNode(0)
|
||||
hash_obj = hash(obj)
|
||||
print(f"Хеш-значение объекта узла {obj} = {hash_obj}")
|
||||
50
ru/codes/python/chapter_hashing/hash_map.py
Normal file
50
ru/codes/python/chapter_hashing/hash_map.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
File: hash_map.py
|
||||
Created Time: 2022-12-14
|
||||
Author: msk397 (machangxinq@gmail.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import print_dict
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация хеш-таблицы
|
||||
hmap = dict[int, str]()
|
||||
|
||||
# Операция добавления
|
||||
# Добавить пару (key, value) в хеш-таблицу
|
||||
hmap[12836] = "Сяо Ха"
|
||||
hmap[15937] = "Сяо Ло"
|
||||
hmap[16750] = "Сяо Суань"
|
||||
hmap[13276] = "Сяо Фа"
|
||||
hmap[10583] = "Сяо Я"
|
||||
print("\nПосле добавления хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
print_dict(hmap)
|
||||
|
||||
# Операция поиска
|
||||
# Передать ключ key в хеш-таблицу и получить значение value
|
||||
name: str = hmap[15937]
|
||||
print("\nДля номера 15937 найдено имя " + name)
|
||||
|
||||
# Операция удаления
|
||||
# Удалить пару (key, value) из хеш-таблицы
|
||||
hmap.pop(10583)
|
||||
print("\nПосле удаления 10583 хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
print_dict(hmap)
|
||||
|
||||
# Обход хеш-таблицы
|
||||
print("\nОтдельный обход пар ключ-значение")
|
||||
for key, value in hmap.items():
|
||||
print(key, "->", value)
|
||||
|
||||
print("\nОтдельный обход ключей")
|
||||
for key in hmap.keys():
|
||||
print(key)
|
||||
|
||||
print("\nОтдельный обход значений")
|
||||
for val in hmap.values():
|
||||
print(val)
|
||||
118
ru/codes/python/chapter_hashing/hash_map_chaining.py
Normal file
118
ru/codes/python/chapter_hashing/hash_map_chaining.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
File: hash_map_chaining.py
|
||||
Created Time: 2023-06-13
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from chapter_hashing.array_hash_map import Pair
|
||||
|
||||
|
||||
class HashMapChaining:
|
||||
"""Хеш-таблица с цепочками"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
self.size = 0 # Число пар ключ-значение
|
||||
self.capacity = 4 # Вместимость хеш-таблицы
|
||||
self.load_thres = 2.0 / 3.0 # Порог коэффициента загрузки для запуска расширения
|
||||
self.extend_ratio = 2 # Коэффициент расширения
|
||||
self.buckets = [[] for _ in range(self.capacity)] # Массив корзин
|
||||
|
||||
def hash_func(self, key: int) -> int:
|
||||
"""Хеш-функция"""
|
||||
return key % self.capacity
|
||||
|
||||
def load_factor(self) -> float:
|
||||
"""Коэффициент загрузки"""
|
||||
return self.size / self.capacity
|
||||
|
||||
def get(self, key: int) -> str | None:
|
||||
"""Операция поиска"""
|
||||
index = self.hash_func(key)
|
||||
bucket = self.buckets[index]
|
||||
# Обойти корзину; если найден key, вернуть соответствующее val
|
||||
for pair in bucket:
|
||||
if pair.key == key:
|
||||
return pair.val
|
||||
# Если key не найден, вернуть None
|
||||
return None
|
||||
|
||||
def put(self, key: int, val: str):
|
||||
"""Операция добавления"""
|
||||
# Когда коэффициент загрузки превышает порог, выполнить расширение
|
||||
if self.load_factor() > self.load_thres:
|
||||
self.extend()
|
||||
index = self.hash_func(key)
|
||||
bucket = self.buckets[index]
|
||||
# Обойти корзину; если встретился указанный key, обновить соответствующее val и вернуть
|
||||
for pair in bucket:
|
||||
if pair.key == key:
|
||||
pair.val = val
|
||||
return
|
||||
# Если такого key нет, добавить пару ключ-значение в конец
|
||||
pair = Pair(key, val)
|
||||
bucket.append(pair)
|
||||
self.size += 1
|
||||
|
||||
def remove(self, key: int):
|
||||
"""Операция удаления"""
|
||||
index = self.hash_func(key)
|
||||
bucket = self.buckets[index]
|
||||
# Обойти корзину и удалить из нее пару ключ-значение
|
||||
for pair in bucket:
|
||||
if pair.key == key:
|
||||
bucket.remove(pair)
|
||||
self.size -= 1
|
||||
break
|
||||
|
||||
def extend(self):
|
||||
"""Расширить хеш-таблицу"""
|
||||
# Временно сохранить исходную хеш-таблицу
|
||||
buckets = self.buckets
|
||||
# Инициализация новой хеш-таблицы после расширения
|
||||
self.capacity *= self.extend_ratio
|
||||
self.buckets = [[] for _ in range(self.capacity)]
|
||||
self.size = 0
|
||||
# Перенести пары ключ-значение из исходной хеш-таблицы в новую
|
||||
for bucket in buckets:
|
||||
for pair in bucket:
|
||||
self.put(pair.key, pair.val)
|
||||
|
||||
def print(self):
|
||||
"""Вывести хеш-таблицу"""
|
||||
for bucket in self.buckets:
|
||||
res = []
|
||||
for pair in bucket:
|
||||
res.append(str(pair.key) + " -> " + pair.val)
|
||||
print(res)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация хеш-таблицы
|
||||
hashmap = HashMapChaining()
|
||||
|
||||
# Операция добавления
|
||||
# Добавить пару (key, value) в хеш-таблицу
|
||||
hashmap.put(12836, "Сяо Ха")
|
||||
hashmap.put(15937, "Сяо Ло")
|
||||
hashmap.put(16750, "Сяо Суань")
|
||||
hashmap.put(13276, "Сяо Фа")
|
||||
hashmap.put(10583, "Сяо Я")
|
||||
print("\nПосле завершения добавления хеш-таблица имеет вид\n[Key1 -> Value1, Key2 -> Value2, ...]")
|
||||
hashmap.print()
|
||||
|
||||
# Операция поиска
|
||||
# Передать ключ key в хеш-таблицу и получить значение value
|
||||
name = hashmap.get(13276)
|
||||
print("\nДля номера 13276 найдено имя " + name)
|
||||
|
||||
# Операция удаления
|
||||
# Удалить пару (key, value) из хеш-таблицы
|
||||
hashmap.remove(12836)
|
||||
print("\nПосле удаления 12836 хеш-таблица имеет вид\n[Key1 -> Value1, Key2 -> Value2, ...]")
|
||||
hashmap.print()
|
||||
138
ru/codes/python/chapter_hashing/hash_map_open_addressing.py
Normal file
138
ru/codes/python/chapter_hashing/hash_map_open_addressing.py
Normal file
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
File: hash_map_open_addressing.py
|
||||
Created Time: 2023-06-13
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from chapter_hashing.array_hash_map import Pair
|
||||
|
||||
|
||||
class HashMapOpenAddressing:
|
||||
"""Хеш-таблица с открытой адресацией"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
self.size = 0 # Число пар ключ-значение
|
||||
self.capacity = 4 # Вместимость хеш-таблицы
|
||||
self.load_thres = 2.0 / 3.0 # Порог коэффициента загрузки для запуска расширения
|
||||
self.extend_ratio = 2 # Коэффициент расширения
|
||||
self.buckets: list[Pair | None] = [None] * self.capacity # Массив корзин
|
||||
self.TOMBSTONE = Pair(-1, "-1") # Удалить метку
|
||||
|
||||
def hash_func(self, key: int) -> int:
|
||||
"""Хеш-функция"""
|
||||
return key % self.capacity
|
||||
|
||||
def load_factor(self) -> float:
|
||||
"""Коэффициент загрузки"""
|
||||
return self.size / self.capacity
|
||||
|
||||
def find_bucket(self, key: int) -> int:
|
||||
"""Найти индекс корзины, соответствующий key"""
|
||||
index = self.hash_func(key)
|
||||
first_tombstone = -1
|
||||
# Выполнять линейное пробирование и завершить при встрече с пустой корзиной
|
||||
while self.buckets[index] is not None:
|
||||
# Если встретился key, вернуть соответствующий индекс корзины
|
||||
if self.buckets[index].key == key:
|
||||
# Если ранее встретилась метка удаления, переместить пару ключ-значение на этот индекс
|
||||
if first_tombstone != -1:
|
||||
self.buckets[first_tombstone] = self.buckets[index]
|
||||
self.buckets[index] = self.TOMBSTONE
|
||||
return first_tombstone # Вернуть индекс корзины после перемещения
|
||||
return index # Вернуть индекс корзины
|
||||
# Записать первую встретившуюся метку удаления
|
||||
if first_tombstone == -1 and self.buckets[index] is self.TOMBSTONE:
|
||||
first_tombstone = index
|
||||
# Вычислить индекс корзины; при выходе за конец вернуться к началу
|
||||
index = (index + 1) % self.capacity
|
||||
# Если key не существует, вернуть индекс точки добавления
|
||||
return index if first_tombstone == -1 else first_tombstone
|
||||
|
||||
def get(self, key: int) -> str:
|
||||
"""Операция поиска"""
|
||||
# Найти индекс корзины, соответствующий key
|
||||
index = self.find_bucket(key)
|
||||
# Если пара ключ-значение найдена, вернуть соответствующее val
|
||||
if self.buckets[index] not in [None, self.TOMBSTONE]:
|
||||
return self.buckets[index].val
|
||||
# Если пара ключ-значение не существует, вернуть None
|
||||
return None
|
||||
|
||||
def put(self, key: int, val: str):
|
||||
"""Операция добавления"""
|
||||
# Когда коэффициент загрузки превышает порог, выполнить расширение
|
||||
if self.load_factor() > self.load_thres:
|
||||
self.extend()
|
||||
# Найти индекс корзины, соответствующий key
|
||||
index = self.find_bucket(key)
|
||||
# Если пара ключ-значение найдена, перезаписать val и вернуть
|
||||
if self.buckets[index] not in [None, self.TOMBSTONE]:
|
||||
self.buckets[index].val = val
|
||||
return
|
||||
# Если пары ключ-значение нет, добавить ее
|
||||
self.buckets[index] = Pair(key, val)
|
||||
self.size += 1
|
||||
|
||||
def remove(self, key: int):
|
||||
"""Операция удаления"""
|
||||
# Найти индекс корзины, соответствующий key
|
||||
index = self.find_bucket(key)
|
||||
# Если пара ключ-значение найдена, заменить ее меткой удаления
|
||||
if self.buckets[index] not in [None, self.TOMBSTONE]:
|
||||
self.buckets[index] = self.TOMBSTONE
|
||||
self.size -= 1
|
||||
|
||||
def extend(self):
|
||||
"""Расширить хеш-таблицу"""
|
||||
# Временно сохранить исходную хеш-таблицу
|
||||
buckets_tmp = self.buckets
|
||||
# Инициализация новой хеш-таблицы после расширения
|
||||
self.capacity *= self.extend_ratio
|
||||
self.buckets = [None] * self.capacity
|
||||
self.size = 0
|
||||
# Перенести пары ключ-значение из исходной хеш-таблицы в новую
|
||||
for pair in buckets_tmp:
|
||||
if pair not in [None, self.TOMBSTONE]:
|
||||
self.put(pair.key, pair.val)
|
||||
|
||||
def print(self):
|
||||
"""Вывести хеш-таблицу"""
|
||||
for pair in self.buckets:
|
||||
if pair is None:
|
||||
print("None")
|
||||
elif pair is self.TOMBSTONE:
|
||||
print("TOMBSTONE")
|
||||
else:
|
||||
print(pair.key, "->", pair.val)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация хеш-таблицы
|
||||
hashmap = HashMapOpenAddressing()
|
||||
|
||||
# Операция добавления
|
||||
# Добавить пару (key, val) в хеш-таблицу
|
||||
hashmap.put(12836, "Сяо Ха")
|
||||
hashmap.put(15937, "Сяо Ло")
|
||||
hashmap.put(16750, "Сяо Суань")
|
||||
hashmap.put(13276, "Сяо Фа")
|
||||
hashmap.put(10583, "Сяо Я")
|
||||
print("\nПосле добавления хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
hashmap.print()
|
||||
|
||||
# Операция поиска
|
||||
# Передать ключ key в хеш-таблицу и получить значение val
|
||||
name = hashmap.get(13276)
|
||||
print("\nДля номера 13276 найдено имя " + name)
|
||||
|
||||
# Операция удаления
|
||||
# Удалить пару (key, val) из хеш-таблицы
|
||||
hashmap.remove(16750)
|
||||
print("\nПосле удаления 16750 хеш-таблица имеет вид\nКлюч -> Значение")
|
||||
hashmap.print()
|
||||
58
ru/codes/python/chapter_hashing/simple_hash.py
Normal file
58
ru/codes/python/chapter_hashing/simple_hash.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
File: simple_hash.py
|
||||
Created Time: 2023-06-15
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def add_hash(key: str) -> int:
|
||||
"""Аддитивное хеширование"""
|
||||
hash = 0
|
||||
modulus = 1000000007
|
||||
for c in key:
|
||||
hash += ord(c)
|
||||
return hash % modulus
|
||||
|
||||
|
||||
def mul_hash(key: str) -> int:
|
||||
"""Мультипликативное хеширование"""
|
||||
hash = 0
|
||||
modulus = 1000000007
|
||||
for c in key:
|
||||
hash = 31 * hash + ord(c)
|
||||
return hash % modulus
|
||||
|
||||
|
||||
def xor_hash(key: str) -> int:
|
||||
"""XOR-хеширование"""
|
||||
hash = 0
|
||||
modulus = 1000000007
|
||||
for c in key:
|
||||
hash ^= ord(c)
|
||||
return hash % modulus
|
||||
|
||||
|
||||
def rot_hash(key: str) -> int:
|
||||
"""Хеширование с циклическим сдвигом"""
|
||||
hash = 0
|
||||
modulus = 1000000007
|
||||
for c in key:
|
||||
hash = (hash << 4) ^ (hash >> 28) ^ ord(c)
|
||||
return hash % modulus
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
key = "Hello Algo"
|
||||
|
||||
hash = add_hash(key)
|
||||
print(f"Хеш-сумма сложением = {hash}")
|
||||
|
||||
hash = mul_hash(key)
|
||||
print(f"Хеш-сумма умножением = {hash}")
|
||||
|
||||
hash = xor_hash(key)
|
||||
print(f"Хеш-сумма XOR = {hash}")
|
||||
|
||||
hash = rot_hash(key)
|
||||
print(f"Хеш-сумма с циклическим сдвигом = {hash}")
|
||||
71
ru/codes/python/chapter_heap/heap.py
Normal file
71
ru/codes/python/chapter_heap/heap.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
File: heap.py
|
||||
Created Time: 2023-02-23
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import print_heap
|
||||
|
||||
import heapq
|
||||
|
||||
|
||||
def test_push(heap: list, val: int, flag: int = 1):
|
||||
heapq.heappush(heap, flag * val) # Добавление элемента в кучу
|
||||
print(f"\nПосле добавления элемента {val} в кучу")
|
||||
print_heap([flag * val for val in heap])
|
||||
|
||||
|
||||
def test_pop(heap: list, flag: int = 1):
|
||||
val = flag * heapq.heappop(heap) # Извлечение элемента с вершины кучи
|
||||
print(f"\nПосле извлечения элемента вершины кучи {val}")
|
||||
print_heap([flag * val for val in heap])
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация минимальной кучи
|
||||
min_heap, flag = [], 1
|
||||
# Инициализация максимальной кучи
|
||||
max_heap, flag = [], -1
|
||||
|
||||
print("\nНиже приведен тестовый пример для max-heap")
|
||||
# Модуль heapq в Python по умолчанию реализует минимальную кучу
|
||||
# Можно помещать в кучу отрицательные элементы, чтобы инвертировать отношение порядка и таким образом реализовать максимальную кучу
|
||||
# В этом примере flag = 1 соответствует минимальной куче, а flag = -1 — максимальной куче
|
||||
|
||||
# Добавление элемента в кучу
|
||||
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)
|
||||
|
||||
# Получение элемента с вершины кучи
|
||||
peek: int = flag * max_heap[0]
|
||||
print(f"\nЭлемент на вершине кучи = {peek}")
|
||||
|
||||
# Извлечение элемента с вершины кучи
|
||||
test_pop(max_heap, flag)
|
||||
test_pop(max_heap, flag)
|
||||
test_pop(max_heap, flag)
|
||||
test_pop(max_heap, flag)
|
||||
test_pop(max_heap, flag)
|
||||
|
||||
# Получение размера кучи
|
||||
size: int = len(max_heap)
|
||||
print(f"\nКоличество элементов в куче = {size}")
|
||||
|
||||
# Проверка, пуста ли куча
|
||||
is_empty: bool = not max_heap
|
||||
print(f"\nПуста ли куча: {is_empty}")
|
||||
|
||||
# Создать кучу по входному списку
|
||||
# Временная сложность равна O(n), а не O(nlogn)
|
||||
min_heap = [1, 3, 2, 5, 4]
|
||||
heapq.heapify(min_heap)
|
||||
print("\nПосле построения min-heap из входного списка")
|
||||
print_heap(min_heap)
|
||||
137
ru/codes/python/chapter_heap/my_heap.py
Normal file
137
ru/codes/python/chapter_heap/my_heap.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
File: my_heap.py
|
||||
Created Time: 2023-02-23
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import print_heap
|
||||
|
||||
|
||||
class MaxHeap:
|
||||
"""Максимальная куча"""
|
||||
|
||||
def __init__(self, nums: list[int]):
|
||||
"""Конструктор, строящий кучу по входному списку"""
|
||||
# Добавить элементы списка в кучу без изменений
|
||||
self.max_heap = nums
|
||||
# Выполнить heapify для всех узлов, кроме листовых
|
||||
for i in range(self.parent(self.size() - 1), -1, -1):
|
||||
self.sift_down(i)
|
||||
|
||||
def left(self, i: int) -> int:
|
||||
"""Получить индекс левого дочернего узла"""
|
||||
return 2 * i + 1
|
||||
|
||||
def right(self, i: int) -> int:
|
||||
"""Получить индекс правого дочернего узла"""
|
||||
return 2 * i + 2
|
||||
|
||||
def parent(self, i: int) -> int:
|
||||
"""Получить индекс родительского узла"""
|
||||
return (i - 1) // 2 # Округление вниз при делении
|
||||
|
||||
def swap(self, i: int, j: int):
|
||||
"""Поменять элементы местами"""
|
||||
self.max_heap[i], self.max_heap[j] = self.max_heap[j], self.max_heap[i]
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получение размера кучи"""
|
||||
return len(self.max_heap)
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""Проверка, пуста ли куча"""
|
||||
return self.size() == 0
|
||||
|
||||
def peek(self) -> int:
|
||||
"""Доступ к элементу на вершине кучи"""
|
||||
return self.max_heap[0]
|
||||
|
||||
def push(self, val: int):
|
||||
"""Добавление элемента в кучу"""
|
||||
# Добавление узла
|
||||
self.max_heap.append(val)
|
||||
# Просеивание снизу вверх
|
||||
self.sift_up(self.size() - 1)
|
||||
|
||||
def sift_up(self, i: int):
|
||||
"""Начиная с узла i, выполнить просеивание снизу вверх"""
|
||||
while True:
|
||||
# Получение родительского узла для узла i
|
||||
p = self.parent(i)
|
||||
# Завершить heapify, когда «корневой узел уже пройден» или «узел не требует исправления»
|
||||
if p < 0 or self.max_heap[i] <= self.max_heap[p]:
|
||||
break
|
||||
# Поменять два узла местами
|
||||
self.swap(i, p)
|
||||
# Циклическое просеивание вверх
|
||||
i = p
|
||||
|
||||
def pop(self) -> int:
|
||||
"""Извлечение элемента из кучи"""
|
||||
# Обработка пустого случая
|
||||
if self.is_empty():
|
||||
raise IndexError("куча пуста")
|
||||
# Поменять корневой узел с самым правым листом местами (поменять первый и последний элементы)
|
||||
self.swap(0, self.size() - 1)
|
||||
# Удаление узла
|
||||
val = self.max_heap.pop()
|
||||
# Просеивание сверху вниз
|
||||
self.sift_down(0)
|
||||
# Вернуть элемент с вершины кучи
|
||||
return val
|
||||
|
||||
def sift_down(self, i: int):
|
||||
"""Начиная с узла i, выполнить просеивание сверху вниз"""
|
||||
while True:
|
||||
# Определить узел с максимальным значением среди i, l и r и обозначить его как 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
|
||||
# Если узел i уже максимален или индексы l и r вне границ, дальнейшее просеивание не требуется, выйти
|
||||
if ma == i:
|
||||
break
|
||||
# Поменять два узла местами
|
||||
self.swap(i, ma)
|
||||
# Циклическое просеивание вниз
|
||||
i = ma
|
||||
|
||||
def print(self):
|
||||
"""Вывести кучу (двоичное дерево)"""
|
||||
print_heap(self.max_heap)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация максимальной кучи
|
||||
max_heap = MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2])
|
||||
print("\nПосле построения кучи из входного списка")
|
||||
max_heap.print()
|
||||
|
||||
# Получение элемента с вершины кучи
|
||||
peek = max_heap.peek()
|
||||
print(f"\nЭлемент на вершине кучи = {peek}")
|
||||
|
||||
# Добавление элемента в кучу
|
||||
val = 7
|
||||
max_heap.push(val)
|
||||
print(f"\nПосле добавления элемента {val} в кучу")
|
||||
max_heap.print()
|
||||
|
||||
# Извлечение элемента с вершины кучи
|
||||
peek = max_heap.pop()
|
||||
print(f"\nПосле извлечения элемента вершины кучи {peek}")
|
||||
max_heap.print()
|
||||
|
||||
# Получение размера кучи
|
||||
size = max_heap.size()
|
||||
print(f"\nКоличество элементов в куче = {size}")
|
||||
|
||||
# Проверка, пуста ли куча
|
||||
is_empty = max_heap.is_empty()
|
||||
print(f"\nПуста ли куча: {is_empty}")
|
||||
39
ru/codes/python/chapter_heap/top_k.py
Normal file
39
ru/codes/python/chapter_heap/top_k.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
File: top_k.py
|
||||
Created Time: 2023-06-10
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import print_heap
|
||||
|
||||
import heapq
|
||||
|
||||
|
||||
def top_k_heap(nums: list[int], k: int) -> list[int]:
|
||||
"""Найти k наибольших элементов массива с помощью кучи"""
|
||||
# Инициализация минимальной кучи
|
||||
heap = []
|
||||
# Поместить первые k элементов массива в кучу
|
||||
for i in range(k):
|
||||
heapq.heappush(heap, nums[i])
|
||||
# Начиная с элемента k+1, поддерживать длину кучи равной k
|
||||
for i in range(k, len(nums)):
|
||||
# Если текущий элемент больше элемента на вершине кучи, извлечь вершину кучи и добавить текущий элемент в кучу
|
||||
if nums[i] > heap[0]:
|
||||
heapq.heappop(heap)
|
||||
heapq.heappush(heap, nums[i])
|
||||
return heap
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [1, 7, 6, 3, 2]
|
||||
k = 3
|
||||
|
||||
res = top_k_heap(nums, k)
|
||||
print(f"Наибольшие {k} элементов")
|
||||
print_heap(res)
|
||||
52
ru/codes/python/chapter_searching/binary_search.py
Normal file
52
ru/codes/python/chapter_searching/binary_search.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
File: binary_search.py
|
||||
Created Time: 2022-11-26
|
||||
Author: timi (xisunyy@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def binary_search(nums: list[int], target: int) -> int:
|
||||
"""Бинарный поиск (двусторонне замкнутый интервал)"""
|
||||
# Инициализировать двусторонне замкнутый интервал [0, n-1], то есть i и j указывают на первый и последний элементы массива соответственно
|
||||
i, j = 0, len(nums) - 1
|
||||
# Цикл завершается, когда диапазон поиска пуст (при i > j диапазон пуст)
|
||||
while i <= j:
|
||||
# Теоретически числа в Python могут быть сколь угодно большими (ограничены только объемом памяти), поэтому не нужно учитывать переполнение больших чисел
|
||||
m = (i + j) // 2 # Вычислить индекс середины m
|
||||
if nums[m] < target:
|
||||
i = m + 1 # Это означает, что target находится в интервале [m+1, j]
|
||||
elif nums[m] > target:
|
||||
j = m - 1 # Это означает, что target находится в интервале [i, m-1]
|
||||
else:
|
||||
return m # Целевой элемент найден, вернуть его индекс
|
||||
return -1 # Целевой элемент не найден, вернуть -1
|
||||
|
||||
|
||||
def binary_search_lcro(nums: list[int], target: int) -> int:
|
||||
"""Бинарный поиск (лево замкнутый, право открытый интервал)"""
|
||||
# Инициализировать лево замкнутый, право открытый интервал [0, n), то есть i и j указывают на первый элемент массива и позицию сразу за последним элементом соответственно
|
||||
i, j = 0, len(nums)
|
||||
# Цикл завершается, когда диапазон поиска пуст (при i = j диапазон пуст)
|
||||
while i < j:
|
||||
m = (i + j) // 2 # Вычислить индекс середины m
|
||||
if nums[m] < target:
|
||||
i = m + 1 # Это означает, что target находится в интервале [m+1, j)
|
||||
elif nums[m] > target:
|
||||
j = m # Это означает, что target находится в интервале [i, m)
|
||||
else:
|
||||
return m # Целевой элемент найден, вернуть его индекс
|
||||
return -1 # Целевой элемент не найден, вернуть -1
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
target = 6
|
||||
nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
|
||||
|
||||
# Бинарный поиск (двусторонне замкнутый интервал)
|
||||
index = binary_search(nums, target)
|
||||
print("Индекс целевого элемента 6 = ", index)
|
||||
|
||||
# Бинарный поиск (лево замкнутый, право открытый интервал)
|
||||
index = binary_search_lcro(nums, target)
|
||||
print("Индекс целевого элемента 6 = ", index)
|
||||
49
ru/codes/python/chapter_searching/binary_search_edge.py
Normal file
49
ru/codes/python/chapter_searching/binary_search_edge.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
File: binary_search_edge.py
|
||||
Created Time: 2023-08-04
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from binary_search_insertion import binary_search_insertion
|
||||
|
||||
|
||||
def binary_search_left_edge(nums: list[int], target: int) -> int:
|
||||
"""Бинарный поиск самого левого target"""
|
||||
# Эквивалентно поиску точки вставки target
|
||||
i = binary_search_insertion(nums, target)
|
||||
# target не найден, вернуть -1
|
||||
if i == len(nums) or nums[i] != target:
|
||||
return -1
|
||||
# Найти target и вернуть индекс i
|
||||
return i
|
||||
|
||||
|
||||
def binary_search_right_edge(nums: list[int], target: int) -> int:
|
||||
"""Бинарный поиск самого правого target"""
|
||||
# Преобразовать задачу в поиск самого левого target + 1
|
||||
i = binary_search_insertion(nums, target + 1)
|
||||
# j указывает на самый правый target, а i — на первый элемент больше target
|
||||
j = i - 1
|
||||
# target не найден, вернуть -1
|
||||
if j == -1 or nums[j] != target:
|
||||
return -1
|
||||
# Найти target и вернуть индекс j
|
||||
return j
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Массив с повторяющимися элементами
|
||||
nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]
|
||||
print(f"\nМассив nums = {nums}")
|
||||
|
||||
# Бинарный поиск левой и правой границы
|
||||
for target in [6, 7]:
|
||||
index = binary_search_left_edge(nums, target)
|
||||
print(f"Индекс самого левого элемента {target} равен {index}")
|
||||
index = binary_search_right_edge(nums, target)
|
||||
print(f"Индекс самого правого элемента {target} равен {index}")
|
||||
54
ru/codes/python/chapter_searching/binary_search_insertion.py
Normal file
54
ru/codes/python/chapter_searching/binary_search_insertion.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
File: binary_search_insertion.py
|
||||
Created Time: 2023-08-04
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def binary_search_insertion_simple(nums: list[int], target: int) -> int:
|
||||
"""Бинарный поиск точки вставки (без повторяющихся элементов)"""
|
||||
i, j = 0, len(nums) - 1 # Инициализировать двусторонне замкнутый интервал [0, n-1]
|
||||
while i <= j:
|
||||
m = (i + j) // 2 # Вычислить индекс середины m
|
||||
if nums[m] < target:
|
||||
i = m + 1 # target находится в интервале [m+1, j]
|
||||
elif nums[m] > target:
|
||||
j = m - 1 # target находится в интервале [i, m-1]
|
||||
else:
|
||||
return m # Найти target и вернуть точку вставки m
|
||||
# target не найден, вернуть точку вставки i
|
||||
return i
|
||||
|
||||
|
||||
def binary_search_insertion(nums: list[int], target: int) -> int:
|
||||
"""Бинарный поиск точки вставки (с повторяющимися элементами)"""
|
||||
i, j = 0, len(nums) - 1 # Инициализировать двусторонне замкнутый интервал [0, n-1]
|
||||
while i <= j:
|
||||
m = (i + j) // 2 # Вычислить индекс середины m
|
||||
if nums[m] < target:
|
||||
i = m + 1 # target находится в интервале [m+1, j]
|
||||
elif nums[m] > target:
|
||||
j = m - 1 # target находится в интервале [i, m-1]
|
||||
else:
|
||||
j = m - 1 # Первый элемент меньше target находится в интервале [i, m-1]
|
||||
# Вернуть точку вставки i
|
||||
return i
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Массив без повторяющихся элементов
|
||||
nums = [1, 3, 6, 8, 12, 15, 23, 26, 31, 35]
|
||||
print(f"\nМассив nums = {nums}")
|
||||
# Бинарный поиск точки вставки
|
||||
for target in [6, 9]:
|
||||
index = binary_search_insertion_simple(nums, target)
|
||||
print(f"Индекс позиции вставки элемента {target} равен {index}")
|
||||
|
||||
# Массив с повторяющимися элементами
|
||||
nums = [1, 3, 6, 6, 6, 6, 6, 10, 12, 15]
|
||||
print(f"\nМассив nums = {nums}")
|
||||
# Бинарный поиск точки вставки
|
||||
for target in [2, 6, 20]:
|
||||
index = binary_search_insertion(nums, target)
|
||||
print(f"Индекс позиции вставки элемента {target} равен {index}")
|
||||
51
ru/codes/python/chapter_searching/hashing_search.py
Normal file
51
ru/codes/python/chapter_searching/hashing_search.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
File: hashing_search.py
|
||||
Created Time: 2022-11-26
|
||||
Author: timi (xisunyy@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import ListNode, list_to_linked_list
|
||||
|
||||
|
||||
def hashing_search_array(hmap: dict[int, int], target: int) -> int:
|
||||
"""Хеш-поиск (массив)"""
|
||||
# key хеш-таблицы: целевой элемент, value: индекс
|
||||
# Если такого key нет в хеш-таблице, вернуть -1
|
||||
return hmap.get(target, -1)
|
||||
|
||||
|
||||
def hashing_search_linkedlist(
|
||||
hmap: dict[int, ListNode], target: int
|
||||
) -> ListNode | None:
|
||||
"""Хеш-поиск (связный список)"""
|
||||
# key хеш-таблицы: целевой элемент, value: объект узла
|
||||
# Если такого key нет в хеш-таблице, вернуть None
|
||||
return hmap.get(target, None)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
target = 3
|
||||
|
||||
# Хеш-поиск (массив)
|
||||
nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]
|
||||
# Инициализация хеш-таблицы
|
||||
map0 = dict[int, int]()
|
||||
for i in range(len(nums)):
|
||||
map0[nums[i]] = i # key: элемент, value: индекс
|
||||
index: int = hashing_search_array(map0, target)
|
||||
print("Индекс целевого элемента 3 =", index)
|
||||
|
||||
# Хеш-поиск (связный список)
|
||||
head: ListNode = list_to_linked_list(nums)
|
||||
# Инициализация хеш-таблицы
|
||||
map1 = dict[int, ListNode]()
|
||||
while head:
|
||||
map1[head.val] = head # key: значение узла, value: узел
|
||||
head = head.next
|
||||
node: ListNode = hashing_search_linkedlist(map1, target)
|
||||
print("Объект узла со значением 3 =", node)
|
||||
45
ru/codes/python/chapter_searching/linear_search.py
Normal file
45
ru/codes/python/chapter_searching/linear_search.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
File: linear_search.py
|
||||
Created Time: 2022-11-26
|
||||
Author: timi (xisunyy@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import ListNode, list_to_linked_list
|
||||
|
||||
|
||||
def linear_search_array(nums: list[int], target: int) -> int:
|
||||
"""Линейный поиск (массив)"""
|
||||
# Обход массива
|
||||
for i in range(len(nums)):
|
||||
if nums[i] == target: # Целевой элемент найден, вернуть его индекс
|
||||
return i
|
||||
return -1 # Целевой элемент не найден, вернуть -1
|
||||
|
||||
|
||||
def linear_search_linkedlist(head: ListNode, target: int) -> ListNode | None:
|
||||
"""Линейный поиск (связный список)"""
|
||||
# Обойти связный список
|
||||
while head:
|
||||
if head.val == target: # Найти целевой узел и вернуть его
|
||||
return head
|
||||
head = head.next
|
||||
return None # Целевой узел не найден, вернуть None
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
target = 3
|
||||
|
||||
# Выполнить линейный поиск в массиве
|
||||
nums = [1, 5, 3, 2, 4, 7, 5, 9, 10, 8]
|
||||
index: int = linear_search_array(nums, target)
|
||||
print("Индекс целевого элемента 3 =", index)
|
||||
|
||||
# Выполнить линейный поиск в связном списке
|
||||
head: ListNode = list_to_linked_list(nums)
|
||||
node: ListNode | None = linear_search_linkedlist(head, target)
|
||||
print("Объект узла со значением 3 =", node)
|
||||
42
ru/codes/python/chapter_searching/two_sum.py
Normal file
42
ru/codes/python/chapter_searching/two_sum.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
File: two_sum.py
|
||||
Created Time: 2022-11-25
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def two_sum_brute_force(nums: list[int], target: int) -> list[int]:
|
||||
"""Метод 1: полный перебор"""
|
||||
# Два вложенных цикла, временная сложность O(n^2)
|
||||
for i in range(len(nums) - 1):
|
||||
for j in range(i + 1, len(nums)):
|
||||
if nums[i] + nums[j] == target:
|
||||
return [i, j]
|
||||
return []
|
||||
|
||||
|
||||
def two_sum_hash_table(nums: list[int], target: int) -> list[int]:
|
||||
"""Метод 2: вспомогательная хеш-таблица"""
|
||||
# Вспомогательная хеш-таблица, пространственная сложность O(n)
|
||||
dic = {}
|
||||
# Один цикл, временная сложность O(n)
|
||||
for i in range(len(nums)):
|
||||
if target - nums[i] in dic:
|
||||
return [dic[target - nums[i]], i]
|
||||
dic[nums[i]] = i
|
||||
return []
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# ======= Test Case =======
|
||||
nums = [2, 7, 11, 15]
|
||||
target = 13
|
||||
|
||||
# ====== Основной код ======
|
||||
# Метод 1
|
||||
res: list[int] = two_sum_brute_force(nums, target)
|
||||
print("Результат метода 1 res =", res)
|
||||
# Метод 2
|
||||
res: list[int] = two_sum_hash_table(nums, target)
|
||||
print("Результат метода 2 res =", res)
|
||||
44
ru/codes/python/chapter_sorting/bubble_sort.py
Normal file
44
ru/codes/python/chapter_sorting/bubble_sort.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
File: bubble_sort.py
|
||||
Created Time: 2022-11-25
|
||||
Author: timi (xisunyy@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def bubble_sort(nums: list[int]):
|
||||
"""Пузырьковая сортировка"""
|
||||
n = len(nums)
|
||||
# Внешний цикл: неотсортированный диапазон [0, i]
|
||||
for i in range(n - 1, 0, -1):
|
||||
# Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
|
||||
for j in range(i):
|
||||
if nums[j] > nums[j + 1]:
|
||||
# Поменять местами nums[j] и nums[j + 1]
|
||||
nums[j], nums[j + 1] = nums[j + 1], nums[j]
|
||||
|
||||
|
||||
def bubble_sort_with_flag(nums: list[int]):
|
||||
"""Пузырьковая сортировка (оптимизация флагом)"""
|
||||
n = len(nums)
|
||||
# Внешний цикл: неотсортированный диапазон [0, i]
|
||||
for i in range(n - 1, 0, -1):
|
||||
flag = False # Инициализировать флаг
|
||||
# Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
|
||||
for j in range(i):
|
||||
if nums[j] > nums[j + 1]:
|
||||
# Поменять местами nums[j] и nums[j + 1]
|
||||
nums[j], nums[j + 1] = nums[j + 1], nums[j]
|
||||
flag = True # Записать обмен элементов
|
||||
if not flag:
|
||||
break # На этой итерации «всплытия» не было ни одного обмена, сразу выйти
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [4, 1, 3, 1, 5, 2]
|
||||
bubble_sort(nums)
|
||||
print("После пузырьковой сортировки nums =", nums)
|
||||
|
||||
nums1 = [4, 1, 3, 1, 5, 2]
|
||||
bubble_sort_with_flag(nums1)
|
||||
print("После пузырьковой сортировки nums =", nums1)
|
||||
35
ru/codes/python/chapter_sorting/bucket_sort.py
Normal file
35
ru/codes/python/chapter_sorting/bucket_sort.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
File: bucket_sort.py
|
||||
Created Time: 2023-03-30
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def bucket_sort(nums: list[float]):
|
||||
"""Сортировка корзинами"""
|
||||
# Инициализировать k = n/2 корзин, предполагая распределение 2 элементов в каждую корзину
|
||||
k = len(nums) // 2
|
||||
buckets = [[] for _ in range(k)]
|
||||
# 1. Распределить элементы массива по корзинам
|
||||
for num in nums:
|
||||
# Входные данные лежат в диапазоне [0, 1); использовать num * k для отображения в диапазон индексов [0, k-1]
|
||||
i = int(num * k)
|
||||
# Добавить num в корзину i
|
||||
buckets[i].append(num)
|
||||
# 2. Выполнить сортировку внутри каждой корзины
|
||||
for bucket in buckets:
|
||||
# Использовать встроенную функцию сортировки; ее также можно заменить другим алгоритмом сортировки
|
||||
bucket.sort()
|
||||
# 3. Обойти корзины и объединить результаты
|
||||
i = 0
|
||||
for bucket in buckets:
|
||||
for num in bucket:
|
||||
nums[i] = num
|
||||
i += 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Пусть входные данные — числа с плавающей точкой из диапазона [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("После сортировки корзинами nums =", nums)
|
||||
62
ru/codes/python/chapter_sorting/counting_sort.py
Normal file
62
ru/codes/python/chapter_sorting/counting_sort.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
File: counting_sort.py
|
||||
Created Time: 2023-03-21
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def counting_sort_naive(nums: list[int]):
|
||||
"""Сортировка подсчетом"""
|
||||
# Простая реализация, не подходит для сортировки объектов
|
||||
# 1. Найти максимальный элемент массива m
|
||||
m = max(nums)
|
||||
# 2. Подсчитать число появлений каждой цифры
|
||||
# counter[num] обозначает число появлений num
|
||||
counter = [0] * (m + 1)
|
||||
for num in nums:
|
||||
counter[num] += 1
|
||||
# 3. Обойти counter и заполнить исходный массив nums элементами
|
||||
i = 0
|
||||
for num in range(m + 1):
|
||||
for _ in range(counter[num]):
|
||||
nums[i] = num
|
||||
i += 1
|
||||
|
||||
|
||||
def counting_sort(nums: list[int]):
|
||||
"""Сортировка подсчетом"""
|
||||
# Полная реализация, позволяет сортировать объекты и является стабильной сортировкой
|
||||
# 1. Найти максимальный элемент массива m
|
||||
m = max(nums)
|
||||
# 2. Подсчитать число появлений каждой цифры
|
||||
# counter[num] обозначает число появлений num
|
||||
counter = [0] * (m + 1)
|
||||
for num in nums:
|
||||
counter[num] += 1
|
||||
# 3. Вычислить префиксные суммы counter и преобразовать «число появлений» в «конечный индекс»
|
||||
# То есть counter[num]-1 — это индекс последнего появления num в res
|
||||
for i in range(m):
|
||||
counter[i + 1] += counter[i]
|
||||
# 4. Обойти nums в обратном порядке и поместить элементы в результирующий массив res
|
||||
# Инициализировать массив res для хранения результата
|
||||
n = len(nums)
|
||||
res = [0] * n
|
||||
for i in range(n - 1, -1, -1):
|
||||
num = nums[i]
|
||||
res[counter[num] - 1] = num # Поместить num по соответствующему индексу
|
||||
counter[num] -= 1 # Уменьшить префиксную сумму на 1, чтобы получить индекс следующего размещения num
|
||||
# Перезаписать исходный массив nums массивом результата res
|
||||
for i in range(n):
|
||||
nums[i] = res[i]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]
|
||||
|
||||
counting_sort_naive(nums)
|
||||
print(f"После сортировки подсчетом (объекты не поддерживаются) nums = {nums}")
|
||||
|
||||
nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]
|
||||
counting_sort(nums1)
|
||||
print(f"После сортировки подсчетом nums1 = {nums1}")
|
||||
45
ru/codes/python/chapter_sorting/heap_sort.py
Normal file
45
ru/codes/python/chapter_sorting/heap_sort.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
File: heap_sort.py
|
||||
Created Time: 2023-05-24
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def sift_down(nums: list[int], n: int, i: int):
|
||||
"""Длина кучи равна n; начиная с узла i, выполнить просеивание сверху вниз"""
|
||||
while True:
|
||||
# Определить узел с максимальным значением среди i, l и r и обозначить его как ma
|
||||
l = 2 * i + 1
|
||||
r = 2 * i + 2
|
||||
ma = i
|
||||
if l < n and nums[l] > nums[ma]:
|
||||
ma = l
|
||||
if r < n and nums[r] > nums[ma]:
|
||||
ma = r
|
||||
# Если узел i уже максимален или индексы l и r вне границ, дальнейшее просеивание не требуется, выйти
|
||||
if ma == i:
|
||||
break
|
||||
# Поменять два узла местами
|
||||
nums[i], nums[ma] = nums[ma], nums[i]
|
||||
# Циклическое просеивание вниз
|
||||
i = ma
|
||||
|
||||
|
||||
def heap_sort(nums: list[int]):
|
||||
"""Сортировка кучей"""
|
||||
# Построение кучи: выполнить heapify для всех узлов, кроме листовых
|
||||
for i in range(len(nums) // 2 - 1, -1, -1):
|
||||
sift_down(nums, len(nums), i)
|
||||
# Извлекать максимальный элемент из кучи в течение n-1 итераций
|
||||
for i in range(len(nums) - 1, 0, -1):
|
||||
# Поменять корневой узел с самым правым листом местами (поменять первый и последний элементы)
|
||||
nums[0], nums[i] = nums[i], nums[0]
|
||||
# Начиная с корневого узла, выполнить просеивание сверху вниз
|
||||
sift_down(nums, i, 0)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [4, 1, 3, 1, 5, 2]
|
||||
heap_sort(nums)
|
||||
print("После сортировки кучей nums =", nums)
|
||||
25
ru/codes/python/chapter_sorting/insertion_sort.py
Normal file
25
ru/codes/python/chapter_sorting/insertion_sort.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
File: insertion_sort.py
|
||||
Created Time: 2022-11-25
|
||||
Author: timi (xisunyy@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def insertion_sort(nums: list[int]):
|
||||
"""Сортировка вставками"""
|
||||
# Внешний цикл: отсортированный диапазон [0, i-1]
|
||||
for i in range(1, len(nums)):
|
||||
base = nums[i]
|
||||
j = i - 1
|
||||
# Внутренний цикл: вставить base в правильную позицию отсортированного диапазона [0, i-1]
|
||||
while j >= 0 and nums[j] > base:
|
||||
nums[j + 1] = nums[j] # Сдвинуть nums[j] на одну позицию вправо
|
||||
j -= 1
|
||||
nums[j + 1] = base # Поместить base в правильную позицию
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [4, 1, 3, 1, 5, 2]
|
||||
insertion_sort(nums)
|
||||
print("После сортировки вставками nums =", nums)
|
||||
55
ru/codes/python/chapter_sorting/merge_sort.py
Normal file
55
ru/codes/python/chapter_sorting/merge_sort.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
File: merge_sort.py
|
||||
Created Time: 2022-11-25
|
||||
Author: timi (xisunyy@163.com), krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def merge(nums: list[int], left: int, mid: int, right: int):
|
||||
"""Объединить левый и правый подмассивы"""
|
||||
# Диапазон левого подмассива: [left, mid], диапазон правого подмассива: [mid+1, right]
|
||||
# Создать временный массив tmp для хранения результата слияния
|
||||
tmp = [0] * (right - left + 1)
|
||||
# Инициализировать начальные индексы левого и правого подмассивов
|
||||
i, j, k = left, mid + 1, 0
|
||||
# Пока в левом и правом подмассивах еще есть элементы, сравнивать их и копировать меньший во временный массив
|
||||
while i <= mid and j <= right:
|
||||
if nums[i] <= nums[j]:
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
else:
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
# Скопировать оставшиеся элементы левого и правого подмассивов во временный массив
|
||||
while i <= mid:
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
k += 1
|
||||
while j <= right:
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
# Скопировать элементы временного массива tmp обратно в соответствующий диапазон исходного массива nums
|
||||
for k in range(0, len(tmp)):
|
||||
nums[left + k] = tmp[k]
|
||||
|
||||
|
||||
def merge_sort(nums: list[int], left: int, right: int):
|
||||
"""Сортировка слиянием"""
|
||||
# Условие завершения
|
||||
if left >= right:
|
||||
return # Завершить рекурсию, когда длина подмассива равна 1
|
||||
# Этап разбиения
|
||||
mid = (left + right) // 2 # Вычислить середину
|
||||
merge_sort(nums, left, mid) # Рекурсивно обработать левый подмассив
|
||||
merge_sort(nums, mid + 1, right) # Рекурсивно обработать правый подмассив
|
||||
# Этап слияния
|
||||
merge(nums, left, mid, right)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [7, 3, 2, 6, 0, 1, 5, 4]
|
||||
merge_sort(nums, 0, len(nums) - 1)
|
||||
print("После сортировки слиянием nums =", nums)
|
||||
129
ru/codes/python/chapter_sorting/quick_sort.py
Normal file
129
ru/codes/python/chapter_sorting/quick_sort.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
File: quick_sort.py
|
||||
Created Time: 2022-11-25
|
||||
Author: timi (xisunyy@163.com)
|
||||
"""
|
||||
|
||||
|
||||
class QuickSort:
|
||||
"""Класс быстрой сортировки"""
|
||||
|
||||
def partition(self, nums: list[int], left: int, right: int) -> int:
|
||||
"""Разбиение с опорными указателями"""
|
||||
# Взять nums[left] в качестве опорного элемента
|
||||
i, j = left, right
|
||||
while i < j:
|
||||
while i < j and nums[j] >= nums[left]:
|
||||
j -= 1 # Идти справа налево в поисках первого элемента меньше опорного
|
||||
while i < j and nums[i] <= nums[left]:
|
||||
i += 1 # Идти слева направо в поисках первого элемента больше опорного
|
||||
# Обмен элементов
|
||||
nums[i], nums[j] = nums[j], nums[i]
|
||||
# Переместить опорный элемент на границу двух подмассивов
|
||||
nums[i], nums[left] = nums[left], nums[i]
|
||||
return i # Вернуть индекс опорного элемента
|
||||
|
||||
def quick_sort(self, nums: list[int], left: int, right: int):
|
||||
"""Быстрая сортировка"""
|
||||
# Завершить рекурсию, когда длина подмассива равна 1
|
||||
if left >= right:
|
||||
return
|
||||
# Разбиение с опорными указателями
|
||||
pivot = self.partition(nums, left, right)
|
||||
# Рекурсивно обработать левый и правый подмассивы
|
||||
self.quick_sort(nums, left, pivot - 1)
|
||||
self.quick_sort(nums, pivot + 1, right)
|
||||
|
||||
|
||||
class QuickSortMedian:
|
||||
"""Класс быстрой сортировки (оптимизация медианным опорным элементом)"""
|
||||
|
||||
def median_three(self, nums: list[int], left: int, mid: int, right: int) -> int:
|
||||
"""Выбрать медиану из трех кандидатов"""
|
||||
l, m, r = nums[left], nums[mid], nums[right]
|
||||
if (l <= m <= r) or (r <= m <= l):
|
||||
return mid # m находится между l и r
|
||||
if (m <= l <= r) or (r <= l <= m):
|
||||
return left # l находится между m и r
|
||||
return right
|
||||
|
||||
def partition(self, nums: list[int], left: int, right: int) -> int:
|
||||
"""Разбиение с опорными указателями (медиана трех)"""
|
||||
# Взять nums[left] в качестве опорного элемента
|
||||
med = self.median_three(nums, left, (left + right) // 2, right)
|
||||
# Переместить медиану в крайний левый элемент массива
|
||||
nums[left], nums[med] = nums[med], nums[left]
|
||||
# Взять nums[left] в качестве опорного элемента
|
||||
i, j = left, right
|
||||
while i < j:
|
||||
while i < j and nums[j] >= nums[left]:
|
||||
j -= 1 # Идти справа налево в поисках первого элемента меньше опорного
|
||||
while i < j and nums[i] <= nums[left]:
|
||||
i += 1 # Идти слева направо в поисках первого элемента больше опорного
|
||||
# Обмен элементов
|
||||
nums[i], nums[j] = nums[j], nums[i]
|
||||
# Переместить опорный элемент на границу двух подмассивов
|
||||
nums[i], nums[left] = nums[left], nums[i]
|
||||
return i # Вернуть индекс опорного элемента
|
||||
|
||||
def quick_sort(self, nums: list[int], left: int, right: int):
|
||||
"""Быстрая сортировка"""
|
||||
# Завершить рекурсию, когда длина подмассива равна 1
|
||||
if left >= right:
|
||||
return
|
||||
# Разбиение с опорными указателями
|
||||
pivot = self.partition(nums, left, right)
|
||||
# Рекурсивно обработать левый и правый подмассивы
|
||||
self.quick_sort(nums, left, pivot - 1)
|
||||
self.quick_sort(nums, pivot + 1, right)
|
||||
|
||||
|
||||
class QuickSortTailCall:
|
||||
"""Класс быстрой сортировки (оптимизация глубины рекурсии)"""
|
||||
|
||||
def partition(self, nums: list[int], left: int, right: int) -> int:
|
||||
"""Разбиение с опорными указателями"""
|
||||
# Взять nums[left] в качестве опорного элемента
|
||||
i, j = left, right
|
||||
while i < j:
|
||||
while i < j and nums[j] >= nums[left]:
|
||||
j -= 1 # Идти справа налево в поисках первого элемента меньше опорного
|
||||
while i < j and nums[i] <= nums[left]:
|
||||
i += 1 # Идти слева направо в поисках первого элемента больше опорного
|
||||
# Обмен элементов
|
||||
nums[i], nums[j] = nums[j], nums[i]
|
||||
# Переместить опорный элемент на границу двух подмассивов
|
||||
nums[i], nums[left] = nums[left], nums[i]
|
||||
return i # Вернуть индекс опорного элемента
|
||||
|
||||
def quick_sort(self, nums: list[int], left: int, right: int):
|
||||
"""Быстрая сортировка (оптимизация глубины рекурсии)"""
|
||||
# Завершить, когда длина подмассива равна 1
|
||||
while left < right:
|
||||
# Операция разбиения с опорными указателями
|
||||
pivot = self.partition(nums, left, right)
|
||||
# Выполнить быструю сортировку для более короткого из двух подмассивов
|
||||
if pivot - left < right - pivot:
|
||||
self.quick_sort(nums, left, pivot - 1) # Рекурсивно отсортировать левый подмассив
|
||||
left = pivot + 1 # Оставшийся неотсортированный диапазон: [pivot + 1, right]
|
||||
else:
|
||||
self.quick_sort(nums, pivot + 1, right) # Рекурсивно отсортировать правый подмассив
|
||||
right = pivot - 1 # Оставшийся неотсортированный диапазон: [left, pivot - 1]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Быстрая сортировка
|
||||
nums = [2, 4, 1, 0, 3, 5]
|
||||
QuickSort().quick_sort(nums, 0, len(nums) - 1)
|
||||
print("После быстрой сортировки nums =", nums)
|
||||
|
||||
# Быстрая сортировка (оптимизация медианным опорным элементом)
|
||||
nums1 = [2, 4, 1, 0, 3, 5]
|
||||
QuickSortMedian().quick_sort(nums1, 0, len(nums1) - 1)
|
||||
print("После быстрой сортировки (оптимизация медианным опорным элементом) nums =", nums1)
|
||||
|
||||
# Быстрая сортировка (оптимизация глубины рекурсии)
|
||||
nums2 = [2, 4, 1, 0, 3, 5]
|
||||
QuickSortTailCall().quick_sort(nums2, 0, len(nums2) - 1)
|
||||
print("После быстрой сортировки (оптимизация глубины рекурсии) nums =", nums2)
|
||||
69
ru/codes/python/chapter_sorting/radix_sort.py
Normal file
69
ru/codes/python/chapter_sorting/radix_sort.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
File: radix_sort.py
|
||||
Created Time: 2023-03-26
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def digit(num: int, exp: int) -> int:
|
||||
"""Получить k-й разряд элемента num, где exp = 10^(k-1)"""
|
||||
# Передача exp вместо k позволяет избежать повторного дорогостоящего вычисления степени
|
||||
return (num // exp) % 10
|
||||
|
||||
|
||||
def counting_sort_digit(nums: list[int], exp: int):
|
||||
"""Сортировка подсчетом (сортировка по k-му разряду nums)"""
|
||||
# Разряды десятичной системы лежат в диапазоне 0~9, поэтому нужен массив корзин длины 10
|
||||
counter = [0] * 10
|
||||
n = len(nums)
|
||||
# Подсчитать число появлений каждой цифры от 0 до 9
|
||||
for i in range(n):
|
||||
d = digit(nums[i], exp) # Получить k-й разряд nums[i], обозначив его как d
|
||||
counter[d] += 1 # Подсчитать число появлений цифры d
|
||||
# Вычислить префиксные суммы и преобразовать «число появлений» в «индекс массива»
|
||||
for i in range(1, 10):
|
||||
counter[i] += counter[i - 1]
|
||||
# Выполняя обратный проход, заполнить res элементами по статистике в корзинах
|
||||
res = [0] * n
|
||||
for i in range(n - 1, -1, -1):
|
||||
d = digit(nums[i], exp)
|
||||
j = counter[d] - 1 # Получить индекс j цифры d в массиве
|
||||
res[j] = nums[i] # Поместить текущий элемент по индексу j
|
||||
counter[d] -= 1 # Уменьшить количество d на 1
|
||||
# Перезаписать исходный массив nums результатом
|
||||
for i in range(n):
|
||||
nums[i] = res[i]
|
||||
|
||||
|
||||
def radix_sort(nums: list[int]):
|
||||
"""Поразрядная сортировка"""
|
||||
# Получить максимальный элемент массива, чтобы определить максимальное число разрядов
|
||||
m = max(nums)
|
||||
# Проходить разряды от младшего к старшему
|
||||
exp = 1
|
||||
while exp <= m:
|
||||
# Выполнить сортировку подсчетом по k-му разряду элементов массива
|
||||
# k = 1 -> exp = 1
|
||||
# k = 2 -> exp = 10
|
||||
# то есть exp = 10^(k-1)
|
||||
counting_sort_digit(nums, exp)
|
||||
exp *= 10
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Поразрядная сортировка
|
||||
nums = [
|
||||
10546151,
|
||||
35663510,
|
||||
42865989,
|
||||
34862445,
|
||||
81883077,
|
||||
88906420,
|
||||
72429244,
|
||||
30524779,
|
||||
82060337,
|
||||
63832996,
|
||||
]
|
||||
radix_sort(nums)
|
||||
print("После поразрядной сортировки nums =", nums)
|
||||
26
ru/codes/python/chapter_sorting/selection_sort.py
Normal file
26
ru/codes/python/chapter_sorting/selection_sort.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
File: selection_sort.py
|
||||
Created Time: 2023-05-22
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
def selection_sort(nums: list[int]):
|
||||
"""Сортировка выбором"""
|
||||
n = len(nums)
|
||||
# Внешний цикл: неотсортированный диапазон [i, n-1]
|
||||
for i in range(n - 1):
|
||||
# Внутренний цикл: найти минимальный элемент в неотсортированном диапазоне
|
||||
k = i
|
||||
for j in range(i + 1, n):
|
||||
if nums[j] < nums[k]:
|
||||
k = j # Записать индекс минимального элемента
|
||||
# Поменять этот минимальный элемент местами с первым элементом неотсортированного диапазона
|
||||
nums[i], nums[k] = nums[k], nums[i]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
nums = [4, 1, 3, 1, 5, 2]
|
||||
selection_sort(nums)
|
||||
print("После сортировки выбором nums =", nums)
|
||||
129
ru/codes/python/chapter_stack_and_queue/array_deque.py
Normal file
129
ru/codes/python/chapter_stack_and_queue/array_deque.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
File: array_deque.py
|
||||
Created Time: 2023-03-01
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
class ArrayDeque:
|
||||
"""Двусторонняя очередь на основе кольцевого массива"""
|
||||
|
||||
def __init__(self, capacity: int):
|
||||
"""Конструктор"""
|
||||
self._nums: list[int] = [0] * capacity
|
||||
self._front: int = 0
|
||||
self._size: int = 0
|
||||
|
||||
def capacity(self) -> int:
|
||||
"""Получить вместимость двусторонней очереди"""
|
||||
return len(self._nums)
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получение длины двусторонней очереди"""
|
||||
return self._size
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""Проверка, пуста ли двусторонняя очередь"""
|
||||
return self._size == 0
|
||||
|
||||
def index(self, i: int) -> int:
|
||||
"""Вычислить индекс в кольцевом массиве"""
|
||||
# С помощью операции взятия по модулю соединить начало и конец массива
|
||||
# Когда i выходит за конец массива, он возвращается в начало
|
||||
# Когда i выходит за начало массива, он возвращается в конец
|
||||
return (i + self.capacity()) % self.capacity()
|
||||
|
||||
def push_first(self, num: int):
|
||||
"""Добавление в голову очереди"""
|
||||
if self._size == self.capacity():
|
||||
print("Двусторонняя очередь заполнена")
|
||||
return
|
||||
# Указатель головы сдвигается на одну позицию влево
|
||||
# С помощью операции взятия по модулю front после выхода за начало массива возвращается в хвост
|
||||
self._front = self.index(self._front - 1)
|
||||
# Добавить num в голову очереди
|
||||
self._nums[self._front] = num
|
||||
self._size += 1
|
||||
|
||||
def push_last(self, num: int):
|
||||
"""Добавление в хвост очереди"""
|
||||
if self._size == self.capacity():
|
||||
print("Двусторонняя очередь заполнена")
|
||||
return
|
||||
# Вычислить указатель хвоста, указывающий на индекс хвоста + 1
|
||||
rear = self.index(self._front + self._size)
|
||||
# Добавить num в хвост очереди
|
||||
self._nums[rear] = num
|
||||
self._size += 1
|
||||
|
||||
def pop_first(self) -> int:
|
||||
"""Извлечение из головы очереди"""
|
||||
num = self.peek_first()
|
||||
# Указатель головы сдвигается на одну позицию назад
|
||||
self._front = self.index(self._front + 1)
|
||||
self._size -= 1
|
||||
return num
|
||||
|
||||
def pop_last(self) -> int:
|
||||
"""Извлечение из хвоста очереди"""
|
||||
num = self.peek_last()
|
||||
self._size -= 1
|
||||
return num
|
||||
|
||||
def peek_first(self) -> int:
|
||||
"""Доступ к элементу в начале очереди"""
|
||||
if self.is_empty():
|
||||
raise IndexError("двусторонняя очередь пуста")
|
||||
return self._nums[self._front]
|
||||
|
||||
def peek_last(self) -> int:
|
||||
"""Доступ к элементу в конце очереди"""
|
||||
if self.is_empty():
|
||||
raise IndexError("двусторонняя очередь пуста")
|
||||
# Вычислить индекс хвостового элемента
|
||||
last = self.index(self._front + self._size - 1)
|
||||
return self._nums[last]
|
||||
|
||||
def to_array(self) -> list[int]:
|
||||
"""Вернуть массив для вывода"""
|
||||
# Преобразовывать только элементы списка в пределах фактической длины
|
||||
res = []
|
||||
for i in range(self._size):
|
||||
res.append(self._nums[self.index(self._front + i)])
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация двусторонней очереди
|
||||
deque = ArrayDeque(10)
|
||||
deque.push_last(3)
|
||||
deque.push_last(2)
|
||||
deque.push_last(5)
|
||||
print("Двусторонняя очередь deque =", deque.to_array())
|
||||
|
||||
# Доступ к элементу
|
||||
peek_first: int = deque.peek_first()
|
||||
print("Первый элемент peek_first =", peek_first)
|
||||
peek_last: int = deque.peek_last()
|
||||
print("Последний элемент peek_last =", peek_last)
|
||||
|
||||
# Добавление элемента в очередь
|
||||
deque.push_last(4)
|
||||
print("После добавления элемента 4 в хвост deque =", deque.to_array())
|
||||
deque.push_first(1)
|
||||
print("После добавления элемента 1 в голову deque =", deque.to_array())
|
||||
|
||||
# Извлечение элемента из очереди
|
||||
pop_last: int = deque.pop_last()
|
||||
print("Извлеченный из хвоста элемент =", pop_last, ", deque после извлечения из хвоста =", deque.to_array())
|
||||
pop_first: int = deque.pop_first()
|
||||
print("Извлеченный из головы элемент =", pop_first, ", deque после извлечения из головы =", deque.to_array())
|
||||
|
||||
# Получение длины двусторонней очереди
|
||||
size: int = deque.size()
|
||||
print("Длина двусторонней очереди size =", size)
|
||||
|
||||
# Проверка, пуста ли двусторонняя очередь
|
||||
is_empty: bool = deque.is_empty()
|
||||
print("Пуста ли двусторонняя очередь =", is_empty)
|
||||
98
ru/codes/python/chapter_stack_and_queue/array_queue.py
Normal file
98
ru/codes/python/chapter_stack_and_queue/array_queue.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
File: array_queue.py
|
||||
Created Time: 2022-12-01
|
||||
Author: Peng Chen (pengchzn@gmail.com)
|
||||
"""
|
||||
|
||||
|
||||
class ArrayQueue:
|
||||
"""Очередь на основе кольцевого массива"""
|
||||
|
||||
def __init__(self, size: int):
|
||||
"""Конструктор"""
|
||||
self._nums: list[int] = [0] * size # Массив для хранения элементов очереди
|
||||
self._front: int = 0 # Указатель head, указывающий на первый элемент очереди
|
||||
self._size: int = 0 # Длина очереди
|
||||
|
||||
def capacity(self) -> int:
|
||||
"""Получить вместимость очереди"""
|
||||
return len(self._nums)
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получение длины очереди"""
|
||||
return self._size
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""Проверка, пуста ли очередь"""
|
||||
return self._size == 0
|
||||
|
||||
def push(self, num: int):
|
||||
"""Поместить в очередь"""
|
||||
if self._size == self.capacity():
|
||||
raise IndexError("очередь заполнена")
|
||||
# Вычислить указатель хвоста, указывающий на индекс хвоста + 1
|
||||
# С помощью операции взятия по модулю вернуть rear к началу после выхода за конец массива
|
||||
rear: int = (self._front + self._size) % self.capacity()
|
||||
# Добавить num в хвост очереди
|
||||
self._nums[rear] = num
|
||||
self._size += 1
|
||||
|
||||
def pop(self) -> int:
|
||||
"""Извлечь из очереди"""
|
||||
num: int = self.peek()
|
||||
# Указатель head сдвигается на одну позицию назад; если он выходит за конец, то возвращается в начало массива
|
||||
self._front = (self._front + 1) % self.capacity()
|
||||
self._size -= 1
|
||||
return num
|
||||
|
||||
def peek(self) -> int:
|
||||
"""Доступ к элементу в начале очереди"""
|
||||
if self.is_empty():
|
||||
raise IndexError("очередь пуста")
|
||||
return self._nums[self._front]
|
||||
|
||||
def to_list(self) -> list[int]:
|
||||
"""Вернуть список для вывода"""
|
||||
res = [0] * self.size()
|
||||
j: int = self._front
|
||||
for i in range(self.size()):
|
||||
res[i] = self._nums[(j % self.capacity())]
|
||||
j += 1
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация очереди
|
||||
queue = ArrayQueue(10)
|
||||
|
||||
# Добавление элемента в очередь
|
||||
queue.push(1)
|
||||
queue.push(3)
|
||||
queue.push(2)
|
||||
queue.push(5)
|
||||
queue.push(4)
|
||||
print("Очередь queue =", queue.to_list())
|
||||
|
||||
# Доступ к элементу в начале очереди
|
||||
peek: int = queue.peek()
|
||||
print("Первый элемент peek =", peek)
|
||||
|
||||
# Извлечение элемента из очереди
|
||||
pop: int = queue.pop()
|
||||
print("Извлеченный элемент pop =", pop)
|
||||
print("queue после извлечения =", queue.to_list())
|
||||
|
||||
# Получение длины очереди
|
||||
size: int = queue.size()
|
||||
print("Длина очереди size =", size)
|
||||
|
||||
# Проверка, пуста ли очередь
|
||||
is_empty: bool = queue.is_empty()
|
||||
print("Пуста ли очередь =", is_empty)
|
||||
|
||||
# Проверка кольцевого массива
|
||||
for i in range(10):
|
||||
queue.push(i)
|
||||
queue.pop()
|
||||
print("После", i, "-го раунда операций enqueue и dequeue queue =", queue.to_list())
|
||||
72
ru/codes/python/chapter_stack_and_queue/array_stack.py
Normal file
72
ru/codes/python/chapter_stack_and_queue/array_stack.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
File: array_stack.py
|
||||
Created Time: 2022-11-29
|
||||
Author: Peng Chen (pengchzn@gmail.com)
|
||||
"""
|
||||
|
||||
|
||||
class ArrayStack:
|
||||
"""Стек на основе массива"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
self._stack: list[int] = []
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получение длины стека"""
|
||||
return len(self._stack)
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""Проверка, пуст ли стек"""
|
||||
return self.size() == 0
|
||||
|
||||
def push(self, item: int):
|
||||
"""Поместить в стек"""
|
||||
self._stack.append(item)
|
||||
|
||||
def pop(self) -> int:
|
||||
"""Извлечь из стека"""
|
||||
if self.is_empty():
|
||||
raise IndexError("стек пуст")
|
||||
return self._stack.pop()
|
||||
|
||||
def peek(self) -> int:
|
||||
"""Доступ к верхнему элементу стека"""
|
||||
if self.is_empty():
|
||||
raise IndexError("стек пуст")
|
||||
return self._stack[-1]
|
||||
|
||||
def to_list(self) -> list[int]:
|
||||
"""Вернуть список для вывода"""
|
||||
return self._stack
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация стека
|
||||
stack = ArrayStack()
|
||||
|
||||
# Помещение элемента в стек
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
print("Стек stack =", stack.to_list())
|
||||
|
||||
# Доступ к верхнему элементу стека
|
||||
peek: int = stack.peek()
|
||||
print("Верхний элемент peek =", peek)
|
||||
|
||||
# Извлечение элемента из стека
|
||||
pop: int = stack.pop()
|
||||
print("Извлеченный элемент pop =", pop)
|
||||
print("stack после извлечения =", stack.to_list())
|
||||
|
||||
# Получение длины стека
|
||||
size: int = stack.size()
|
||||
print("Длина стека size =", size)
|
||||
|
||||
# Проверка на пустоту
|
||||
is_empty: bool = stack.is_empty()
|
||||
print("Пуст ли стек =", is_empty)
|
||||
42
ru/codes/python/chapter_stack_and_queue/deque.py
Normal file
42
ru/codes/python/chapter_stack_and_queue/deque.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
File: deque.py
|
||||
Created Time: 2022-11-29
|
||||
Author: Peng Chen (pengchzn@gmail.com)
|
||||
"""
|
||||
|
||||
from collections import deque
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация двусторонней очереди
|
||||
deq: deque[int] = deque()
|
||||
|
||||
# Добавление элемента в очередь
|
||||
deq.append(2) # Добавить в хвост очереди
|
||||
deq.append(5)
|
||||
deq.append(4)
|
||||
deq.appendleft(3) # Добавить в голову очереди
|
||||
deq.appendleft(1)
|
||||
print("Двусторонняя очередь deque =", deq)
|
||||
|
||||
# Доступ к элементу
|
||||
front: int = deq[0] # Элемент в голове очереди
|
||||
print("Первый элемент front =", front)
|
||||
rear: int = deq[-1] # Элемент в хвосте очереди
|
||||
print("Последний элемент rear =", rear)
|
||||
|
||||
# Извлечение элемента из очереди
|
||||
pop_front: int = deq.popleft() # Извлечь элемент из головы очереди
|
||||
print("Извлеченный из головы элемент pop_front =", pop_front)
|
||||
print("deque после извлечения из головы =", deq)
|
||||
pop_rear: int = deq.pop() # Извлечь элемент из хвоста очереди
|
||||
print("Извлеченный из хвоста элемент pop_rear =", pop_rear)
|
||||
print("deque после извлечения из хвоста =", deq)
|
||||
|
||||
# Получение длины двусторонней очереди
|
||||
size: int = len(deq)
|
||||
print("Длина двусторонней очереди size =", size)
|
||||
|
||||
# Проверка, пуста ли двусторонняя очередь
|
||||
is_empty: bool = len(deq) == 0
|
||||
print("Пуста ли двусторонняя очередь =", is_empty)
|
||||
151
ru/codes/python/chapter_stack_and_queue/linkedlist_deque.py
Normal file
151
ru/codes/python/chapter_stack_and_queue/linkedlist_deque.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
File: linkedlist_deque.py
|
||||
Created Time: 2023-03-01
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
class ListNode:
|
||||
"""Узел двусвязного списка"""
|
||||
|
||||
def __init__(self, val: int):
|
||||
"""Конструктор"""
|
||||
self.val: int = val
|
||||
self.next: ListNode | None = None # Ссылка на узел-преемник
|
||||
self.prev: ListNode | None = None # Ссылка на узел-предшественник
|
||||
|
||||
|
||||
class LinkedListDeque:
|
||||
"""Двусторонняя очередь на основе двусвязного списка"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
self._front: ListNode | None = None # Головной узел front
|
||||
self._rear: ListNode | None = None # Хвостовой узел rear
|
||||
self._size: int = 0 # Длина двусторонней очереди
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получение длины двусторонней очереди"""
|
||||
return self._size
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""Проверка, пуста ли двусторонняя очередь"""
|
||||
return self._size == 0
|
||||
|
||||
def push(self, num: int, is_front: bool):
|
||||
"""Операция добавления в очередь"""
|
||||
node = ListNode(num)
|
||||
# Если связный список пуст, сделать так, чтобы и front, и rear указывали на node
|
||||
if self.is_empty():
|
||||
self._front = self._rear = node
|
||||
# Операция добавления в голову очереди
|
||||
elif is_front:
|
||||
# Добавить node в голову списка
|
||||
self._front.prev = node
|
||||
node.next = self._front
|
||||
self._front = node # Обновить головной узел
|
||||
# Операция добавления в хвост очереди
|
||||
else:
|
||||
# Добавить node в хвост списка
|
||||
self._rear.next = node
|
||||
node.prev = self._rear
|
||||
self._rear = node # Обновить хвостовой узел
|
||||
self._size += 1 # Обновить длину очереди
|
||||
|
||||
def push_first(self, num: int):
|
||||
"""Добавление в голову очереди"""
|
||||
self.push(num, True)
|
||||
|
||||
def push_last(self, num: int):
|
||||
"""Добавление в хвост очереди"""
|
||||
self.push(num, False)
|
||||
|
||||
def pop(self, is_front: bool) -> int:
|
||||
"""Операция извлечения из очереди"""
|
||||
if self.is_empty():
|
||||
raise IndexError("двусторонняя очередь пуста")
|
||||
# Операция извлечения из головы очереди
|
||||
if is_front:
|
||||
val: int = self._front.val # Временно сохранить значение головного узла
|
||||
# Удалить головной узел
|
||||
fnext: ListNode | None = self._front.next
|
||||
if fnext is not None:
|
||||
fnext.prev = None
|
||||
self._front.next = None
|
||||
self._front = fnext # Обновить головной узел
|
||||
# Операция извлечения из хвоста очереди
|
||||
else:
|
||||
val: int = self._rear.val # Временно сохранить значение хвостового узла
|
||||
# Удалить хвостовой узел
|
||||
rprev: ListNode | None = self._rear.prev
|
||||
if rprev is not None:
|
||||
rprev.next = None
|
||||
self._rear.prev = None
|
||||
self._rear = rprev # Обновить хвостовой узел
|
||||
self._size -= 1 # Обновить длину очереди
|
||||
return val
|
||||
|
||||
def pop_first(self) -> int:
|
||||
"""Извлечение из головы очереди"""
|
||||
return self.pop(True)
|
||||
|
||||
def pop_last(self) -> int:
|
||||
"""Извлечение из хвоста очереди"""
|
||||
return self.pop(False)
|
||||
|
||||
def peek_first(self) -> int:
|
||||
"""Доступ к элементу в начале очереди"""
|
||||
if self.is_empty():
|
||||
raise IndexError("двусторонняя очередь пуста")
|
||||
return self._front.val
|
||||
|
||||
def peek_last(self) -> int:
|
||||
"""Доступ к элементу в конце очереди"""
|
||||
if self.is_empty():
|
||||
raise IndexError("двусторонняя очередь пуста")
|
||||
return self._rear.val
|
||||
|
||||
def to_array(self) -> list[int]:
|
||||
"""Вернуть массив для вывода"""
|
||||
node = self._front
|
||||
res = [0] * self.size()
|
||||
for i in range(self.size()):
|
||||
res[i] = node.val
|
||||
node = node.next
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация двусторонней очереди
|
||||
deque = LinkedListDeque()
|
||||
deque.push_last(3)
|
||||
deque.push_last(2)
|
||||
deque.push_last(5)
|
||||
print("Двусторонняя очередь deque =", deque.to_array())
|
||||
|
||||
# Доступ к элементу
|
||||
peek_first: int = deque.peek_first()
|
||||
print("Первый элемент peek_first =", peek_first)
|
||||
peek_last: int = deque.peek_last()
|
||||
print("Последний элемент peek_last =", peek_last)
|
||||
|
||||
# Добавление элемента в очередь
|
||||
deque.push_last(4)
|
||||
print("После добавления элемента 4 в хвост deque =", deque.to_array())
|
||||
deque.push_first(1)
|
||||
print("После добавления элемента 1 в голову deque =", deque.to_array())
|
||||
|
||||
# Извлечение элемента из очереди
|
||||
pop_last: int = deque.pop_last()
|
||||
print("Извлеченный из хвоста элемент =", pop_last, ", deque после извлечения из хвоста =", deque.to_array())
|
||||
pop_first: int = deque.pop_first()
|
||||
print("Извлеченный из головы элемент =", pop_first, ", deque после извлечения из головы =", deque.to_array())
|
||||
|
||||
# Получение длины двусторонней очереди
|
||||
size: int = deque.size()
|
||||
print("Длина двусторонней очереди size =", size)
|
||||
|
||||
# Проверка, пуста ли двусторонняя очередь
|
||||
is_empty: bool = deque.is_empty()
|
||||
print("Пуста ли двусторонняя очередь =", is_empty)
|
||||
97
ru/codes/python/chapter_stack_and_queue/linkedlist_queue.py
Normal file
97
ru/codes/python/chapter_stack_and_queue/linkedlist_queue.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
File: linkedlist_queue.py
|
||||
Created Time: 2022-12-01
|
||||
Author: Peng Chen (pengchzn@gmail.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import ListNode
|
||||
|
||||
|
||||
class LinkedListQueue:
|
||||
"""Очередь на основе связного списка"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
self._front: ListNode | None = None # Головной узел front
|
||||
self._rear: ListNode | None = None # Хвостовой узел rear
|
||||
self._size: int = 0
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получение длины очереди"""
|
||||
return self._size
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""Проверка, пуста ли очередь"""
|
||||
return self._size == 0
|
||||
|
||||
def push(self, num: int):
|
||||
"""Поместить в очередь"""
|
||||
# Добавить num после хвостового узла
|
||||
node = ListNode(num)
|
||||
# Если очередь пуста, сделать так, чтобы и head, и tail указывали на этот узел
|
||||
if self._front is None:
|
||||
self._front = node
|
||||
self._rear = node
|
||||
# Если очередь не пуста, добавить этот узел после хвостового узла
|
||||
else:
|
||||
self._rear.next = node
|
||||
self._rear = node
|
||||
self._size += 1
|
||||
|
||||
def pop(self) -> int:
|
||||
"""Извлечь из очереди"""
|
||||
num = self.peek()
|
||||
# Удалить головной узел
|
||||
self._front = self._front.next
|
||||
self._size -= 1
|
||||
return num
|
||||
|
||||
def peek(self) -> int:
|
||||
"""Доступ к элементу в начале очереди"""
|
||||
if self.is_empty():
|
||||
raise IndexError("очередь пуста")
|
||||
return self._front.val
|
||||
|
||||
def to_list(self) -> list[int]:
|
||||
"""Преобразовать в список для вывода"""
|
||||
queue = []
|
||||
temp = self._front
|
||||
while temp:
|
||||
queue.append(temp.val)
|
||||
temp = temp.next
|
||||
return queue
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация очереди
|
||||
queue = LinkedListQueue()
|
||||
|
||||
# Добавление элемента в очередь
|
||||
queue.push(1)
|
||||
queue.push(3)
|
||||
queue.push(2)
|
||||
queue.push(5)
|
||||
queue.push(4)
|
||||
print("Очередь queue =", queue.to_list())
|
||||
|
||||
# Доступ к элементу в начале очереди
|
||||
peek: int = queue.peek()
|
||||
print("Первый элемент front =", peek)
|
||||
|
||||
# Извлечение элемента из очереди
|
||||
pop_front: int = queue.pop()
|
||||
print("Извлеченный элемент pop =", pop_front)
|
||||
print("queue после извлечения =", queue.to_list())
|
||||
|
||||
# Получение длины очереди
|
||||
size: int = queue.size()
|
||||
print("Длина очереди size =", size)
|
||||
|
||||
# Проверка, пуста ли очередь
|
||||
is_empty: bool = queue.is_empty()
|
||||
print("Пуста ли очередь =", is_empty)
|
||||
89
ru/codes/python/chapter_stack_and_queue/linkedlist_stack.py
Normal file
89
ru/codes/python/chapter_stack_and_queue/linkedlist_stack.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
File: linkedlist_stack.py
|
||||
Created Time: 2022-11-29
|
||||
Author: Peng Chen (pengchzn@gmail.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import ListNode
|
||||
|
||||
|
||||
class LinkedListStack:
|
||||
"""Стек на основе связного списка"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
self._peek: ListNode | None = None
|
||||
self._size: int = 0
|
||||
|
||||
def size(self) -> int:
|
||||
"""Получение длины стека"""
|
||||
return self._size
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""Проверка, пуст ли стек"""
|
||||
return self._size == 0
|
||||
|
||||
def push(self, val: int):
|
||||
"""Поместить в стек"""
|
||||
node = ListNode(val)
|
||||
node.next = self._peek
|
||||
self._peek = node
|
||||
self._size += 1
|
||||
|
||||
def pop(self) -> int:
|
||||
"""Извлечь из стека"""
|
||||
num = self.peek()
|
||||
self._peek = self._peek.next
|
||||
self._size -= 1
|
||||
return num
|
||||
|
||||
def peek(self) -> int:
|
||||
"""Доступ к верхнему элементу стека"""
|
||||
if self.is_empty():
|
||||
raise IndexError("стек пуст")
|
||||
return self._peek.val
|
||||
|
||||
def to_list(self) -> list[int]:
|
||||
"""Преобразовать в список для вывода"""
|
||||
arr = []
|
||||
node = self._peek
|
||||
while node:
|
||||
arr.append(node.val)
|
||||
node = node.next
|
||||
arr.reverse()
|
||||
return arr
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация стека
|
||||
stack = LinkedListStack()
|
||||
|
||||
# Помещение элемента в стек
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
stack.push(5)
|
||||
stack.push(4)
|
||||
print("Стек stack =", stack.to_list())
|
||||
|
||||
# Доступ к верхнему элементу стека
|
||||
peek: int = stack.peek()
|
||||
print("Верхний элемент peek =", peek)
|
||||
|
||||
# Извлечение элемента из стека
|
||||
pop: int = stack.pop()
|
||||
print("Извлеченный элемент pop =", pop)
|
||||
print("stack после извлечения =", stack.to_list())
|
||||
|
||||
# Получение длины стека
|
||||
size: int = stack.size()
|
||||
print("Длина стека size =", size)
|
||||
|
||||
# Проверка на пустоту
|
||||
is_empty: bool = stack.is_empty()
|
||||
print("Пуст ли стек =", is_empty)
|
||||
39
ru/codes/python/chapter_stack_and_queue/queue.py
Normal file
39
ru/codes/python/chapter_stack_and_queue/queue.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
File: queue.py
|
||||
Created Time: 2022-11-29
|
||||
Author: Peng Chen (pengchzn@gmail.com)
|
||||
"""
|
||||
|
||||
from collections import deque
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализировать очередь
|
||||
# В Python мы обычно рассматриваем двустороннюю очередь deque как очередь
|
||||
# Хотя queue.Queue() — это «настоящий» класс очереди, им не очень удобно пользоваться
|
||||
que: deque[int] = deque()
|
||||
|
||||
# Добавление элемента в очередь
|
||||
que.append(1)
|
||||
que.append(3)
|
||||
que.append(2)
|
||||
que.append(5)
|
||||
que.append(4)
|
||||
print("Очередь que =", que)
|
||||
|
||||
# Доступ к элементу в начале очереди
|
||||
front: int = que[0]
|
||||
print("Первый элемент front =", front)
|
||||
|
||||
# Извлечение элемента из очереди
|
||||
pop: int = que.popleft()
|
||||
print("Извлеченный элемент pop =", pop)
|
||||
print("que после извлечения =", que)
|
||||
|
||||
# Получение длины очереди
|
||||
size: int = len(que)
|
||||
print("Длина очереди size =", size)
|
||||
|
||||
# Проверка, пуста ли очередь
|
||||
is_empty: bool = len(que) == 0
|
||||
print("Пуста ли очередь =", is_empty)
|
||||
36
ru/codes/python/chapter_stack_and_queue/stack.py
Normal file
36
ru/codes/python/chapter_stack_and_queue/stack.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
File: stack.py
|
||||
Created Time: 2022-11-29
|
||||
Author: Peng Chen (pengchzn@gmail.com)
|
||||
"""
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализировать стек
|
||||
# В Python нет встроенного класса стека, поэтому list можно использовать как стек
|
||||
stack: list[int] = []
|
||||
|
||||
# Помещение элемента в стек
|
||||
stack.append(1)
|
||||
stack.append(3)
|
||||
stack.append(2)
|
||||
stack.append(5)
|
||||
stack.append(4)
|
||||
print("Стек stack =", stack)
|
||||
|
||||
# Доступ к верхнему элементу стека
|
||||
peek: int = stack[-1]
|
||||
print("Верхний элемент peek =", peek)
|
||||
|
||||
# Извлечение элемента из стека
|
||||
pop: int = stack.pop()
|
||||
print("Извлеченный элемент pop =", pop)
|
||||
print("stack после извлечения =", stack)
|
||||
|
||||
# Получение длины стека
|
||||
size: int = len(stack)
|
||||
print("Длина стека size =", size)
|
||||
|
||||
# Проверка на пустоту
|
||||
is_empty: bool = len(stack) == 0
|
||||
print("Пуст ли стек =", is_empty)
|
||||
119
ru/codes/python/chapter_tree/array_binary_tree.py
Normal file
119
ru/codes/python/chapter_tree/array_binary_tree.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
File: array_binary_tree.py
|
||||
Created Time: 2023-07-19
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, list_to_tree, print_tree
|
||||
|
||||
|
||||
class ArrayBinaryTree:
|
||||
"""Класс двоичного дерева в массивном представлении"""
|
||||
|
||||
def __init__(self, arr: list[int | None]):
|
||||
"""Конструктор"""
|
||||
self._tree = list(arr)
|
||||
|
||||
def size(self):
|
||||
"""Вместимость списка"""
|
||||
return len(self._tree)
|
||||
|
||||
def val(self, i: int) -> int | None:
|
||||
"""Получить значение узла с индексом i"""
|
||||
# Если индекс выходит за границы, вернуть None, обозначающий пустую позицию
|
||||
if i < 0 or i >= self.size():
|
||||
return None
|
||||
return self._tree[i]
|
||||
|
||||
def left(self, i: int) -> int | None:
|
||||
"""Получить индекс левого дочернего узла узла с индексом i"""
|
||||
return 2 * i + 1
|
||||
|
||||
def right(self, i: int) -> int | None:
|
||||
"""Получить индекс правого дочернего узла узла с индексом i"""
|
||||
return 2 * i + 2
|
||||
|
||||
def parent(self, i: int) -> int | None:
|
||||
"""Получить индекс родительского узла узла с индексом i"""
|
||||
return (i - 1) // 2
|
||||
|
||||
def level_order(self) -> list[int]:
|
||||
"""Обход в ширину"""
|
||||
self.res = []
|
||||
# Непосредственно обходить массив
|
||||
for i in range(self.size()):
|
||||
if self.val(i) is not None:
|
||||
self.res.append(self.val(i))
|
||||
return self.res
|
||||
|
||||
def dfs(self, i: int, order: str):
|
||||
"""Обход в глубину"""
|
||||
if self.val(i) is None:
|
||||
return
|
||||
# Предварительный обход
|
||||
if order == "pre":
|
||||
self.res.append(self.val(i))
|
||||
self.dfs(self.left(i), order)
|
||||
# Симметричный обход
|
||||
if order == "in":
|
||||
self.res.append(self.val(i))
|
||||
self.dfs(self.right(i), order)
|
||||
# Обратный обход
|
||||
if order == "post":
|
||||
self.res.append(self.val(i))
|
||||
|
||||
def pre_order(self) -> list[int]:
|
||||
"""Предварительный обход"""
|
||||
self.res = []
|
||||
self.dfs(0, order="pre")
|
||||
return self.res
|
||||
|
||||
def in_order(self) -> list[int]:
|
||||
"""Симметричный обход"""
|
||||
self.res = []
|
||||
self.dfs(0, order="in")
|
||||
return self.res
|
||||
|
||||
def post_order(self) -> list[int]:
|
||||
"""Обратный обход"""
|
||||
self.res = []
|
||||
self.dfs(0, order="post")
|
||||
return self.res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализировать двоичное дерево
|
||||
# Здесь используется функция, напрямую строящая двоичное дерево из массива
|
||||
arr = [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
|
||||
root = list_to_tree(arr)
|
||||
print("\nИнициализация двоичного дерева\n")
|
||||
print("Массивное представление двоичного дерева:")
|
||||
print(arr)
|
||||
print("Связное представление двоичного дерева:")
|
||||
print_tree(root)
|
||||
|
||||
# Класс двоичного дерева в массивном представлении
|
||||
abt = ArrayBinaryTree(arr)
|
||||
|
||||
# Доступ к узлу
|
||||
i = 1
|
||||
l, r, p = abt.left(i), abt.right(i), abt.parent(i)
|
||||
print(f"\nТекущий узел: индекс = {i}, значение = {abt.val(i)}")
|
||||
print(f"Индекс левого дочернего узла = {l}, значение = {abt.val(l)}")
|
||||
print(f"Индекс правого дочернего узла = {r}, значение = {abt.val(r)}")
|
||||
print(f"Индекс родительского узла = {p}, значение = {abt.val(p)}")
|
||||
|
||||
# Обходить дерево
|
||||
res = abt.level_order()
|
||||
print("\nОбход в ширину:", res)
|
||||
res = abt.pre_order()
|
||||
print("Предварительный обход:", res)
|
||||
res = abt.in_order()
|
||||
print("Симметричный обход:", res)
|
||||
res = abt.post_order()
|
||||
print("Обратный обход:", res)
|
||||
200
ru/codes/python/chapter_tree/avl_tree.py
Normal file
200
ru/codes/python/chapter_tree/avl_tree.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
File: avl_tree.py
|
||||
Created Time: 2022-12-20
|
||||
Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree
|
||||
|
||||
|
||||
class AVLTree:
|
||||
"""AVL-дерево"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
self._root = None
|
||||
|
||||
def get_root(self) -> TreeNode | None:
|
||||
"""Получить корневой узел двоичного дерева"""
|
||||
return self._root
|
||||
|
||||
def height(self, node: TreeNode | None) -> int:
|
||||
"""Получить высоту узла"""
|
||||
# Высота пустого узла равна -1, высота листового узла равна 0
|
||||
if node is not None:
|
||||
return node.height
|
||||
return -1
|
||||
|
||||
def update_height(self, node: TreeNode | None):
|
||||
"""Обновить высоту узла"""
|
||||
# Высота узла равна высоте более высокого поддерева + 1
|
||||
node.height = max([self.height(node.left), self.height(node.right)]) + 1
|
||||
|
||||
def balance_factor(self, node: TreeNode | None) -> int:
|
||||
"""Получить коэффициент баланса"""
|
||||
# Коэффициент баланса пустого узла равен 0
|
||||
if node is None:
|
||||
return 0
|
||||
# Коэффициент баланса узла = высота левого поддерева - высота правого поддерева
|
||||
return self.height(node.left) - self.height(node.right)
|
||||
|
||||
def right_rotate(self, node: TreeNode | None) -> TreeNode | None:
|
||||
"""Операция правого вращения"""
|
||||
child = node.left
|
||||
grand_child = child.right
|
||||
# Выполнить правое вращение узла node вокруг child
|
||||
child.right = node
|
||||
node.left = grand_child
|
||||
# Обновить высоту узла
|
||||
self.update_height(node)
|
||||
self.update_height(child)
|
||||
# Вернуть корневой узел поддерева после вращения
|
||||
return child
|
||||
|
||||
def left_rotate(self, node: TreeNode | None) -> TreeNode | None:
|
||||
"""Операция левого вращения"""
|
||||
child = node.right
|
||||
grand_child = child.left
|
||||
# Выполнить левое вращение узла node вокруг child
|
||||
child.left = node
|
||||
node.right = grand_child
|
||||
# Обновить высоту узла
|
||||
self.update_height(node)
|
||||
self.update_height(child)
|
||||
# Вернуть корневой узел поддерева после вращения
|
||||
return child
|
||||
|
||||
def rotate(self, node: TreeNode | None) -> TreeNode | None:
|
||||
"""Выполнить вращение, чтобы снова сбалансировать поддерево"""
|
||||
# Получить коэффициент баланса узла node
|
||||
balance_factor = self.balance_factor(node)
|
||||
# Левосторонне перекошенное дерево
|
||||
if balance_factor > 1:
|
||||
if self.balance_factor(node.left) >= 0:
|
||||
# Правое вращение
|
||||
return self.right_rotate(node)
|
||||
else:
|
||||
# Сначала левое вращение, затем правое
|
||||
node.left = self.left_rotate(node.left)
|
||||
return self.right_rotate(node)
|
||||
# Правосторонне перекошенное дерево
|
||||
elif balance_factor < -1:
|
||||
if self.balance_factor(node.right) <= 0:
|
||||
# Левое вращение
|
||||
return self.left_rotate(node)
|
||||
else:
|
||||
# Сначала правое вращение, затем левое
|
||||
node.right = self.right_rotate(node.right)
|
||||
return self.left_rotate(node)
|
||||
# Дерево сбалансировано, вращение не требуется, вернуть сразу
|
||||
return node
|
||||
|
||||
def insert(self, val):
|
||||
"""Вставка узла"""
|
||||
self._root = self.insert_helper(self._root, val)
|
||||
|
||||
def insert_helper(self, node: TreeNode | None, val: int) -> TreeNode:
|
||||
"""Рекурсивная вставка узла (вспомогательный метод)"""
|
||||
if node is None:
|
||||
return TreeNode(val)
|
||||
# 1. Найти позицию вставки и вставить узел
|
||||
if val < node.val:
|
||||
node.left = self.insert_helper(node.left, val)
|
||||
elif val > node.val:
|
||||
node.right = self.insert_helper(node.right, val)
|
||||
else:
|
||||
# Повторяющийся узел не вставлять, сразу вернуть
|
||||
return node
|
||||
# Обновить высоту узла
|
||||
self.update_height(node)
|
||||
# 2. Выполнить вращение, чтобы снова сбалансировать поддерево
|
||||
return self.rotate(node)
|
||||
|
||||
def remove(self, val: int):
|
||||
"""Удаление узла"""
|
||||
self._root = self.remove_helper(self._root, val)
|
||||
|
||||
def remove_helper(self, node: TreeNode | None, val: int) -> TreeNode | None:
|
||||
"""Рекурсивное удаление узла (вспомогательный метод)"""
|
||||
if node is None:
|
||||
return None
|
||||
# 1. Найти узел и удалить его
|
||||
if val < node.val:
|
||||
node.left = self.remove_helper(node.left, val)
|
||||
elif val > node.val:
|
||||
node.right = self.remove_helper(node.right, val)
|
||||
else:
|
||||
if node.left is None or node.right is None:
|
||||
child = node.left or node.right
|
||||
# Число дочерних узлов = 0, удалить node и сразу вернуть
|
||||
if child is None:
|
||||
return None
|
||||
# Число дочерних узлов = 1, удалить node напрямую
|
||||
else:
|
||||
node = child
|
||||
else:
|
||||
# Число дочерних узлов = 2, удалить следующий по симметричному обходу узел и заменить им текущий узел
|
||||
temp = node.right
|
||||
while temp.left is not None:
|
||||
temp = temp.left
|
||||
node.right = self.remove_helper(node.right, temp.val)
|
||||
node.val = temp.val
|
||||
# Обновить высоту узла
|
||||
self.update_height(node)
|
||||
# 2. Выполнить вращение, чтобы снова сбалансировать поддерево
|
||||
return self.rotate(node)
|
||||
|
||||
def search(self, val: int) -> TreeNode | None:
|
||||
"""Поиск узла"""
|
||||
cur = self._root
|
||||
# Искать в цикле и выйти после прохода за листовой узел
|
||||
while cur is not None:
|
||||
# Целевой узел находится в правом поддереве cur
|
||||
if cur.val < val:
|
||||
cur = cur.right
|
||||
# Целевой узел находится в левом поддереве cur
|
||||
elif cur.val > val:
|
||||
cur = cur.left
|
||||
# Найти целевой узел и выйти из цикла
|
||||
else:
|
||||
break
|
||||
# Вернуть целевой узел
|
||||
return cur
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
|
||||
def test_insert(tree: AVLTree, val: int):
|
||||
tree.insert(val)
|
||||
print("\nПосле вставки узла {} AVL-дерево имеет вид".format(val))
|
||||
print_tree(tree.get_root())
|
||||
|
||||
def test_remove(tree: AVLTree, val: int):
|
||||
tree.remove(val)
|
||||
print("\nПосле удаления узла {} AVL-дерево имеет вид".format(val))
|
||||
print_tree(tree.get_root())
|
||||
|
||||
# Инициализация пустого AVL-дерева
|
||||
avl_tree = AVLTree()
|
||||
|
||||
# Вставка узла
|
||||
# Обратите внимание, как AVL-дерево сохраняет баланс после вставки узла
|
||||
for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6]:
|
||||
test_insert(avl_tree, val)
|
||||
|
||||
# Вставка повторяющегося узла
|
||||
test_insert(avl_tree, 7)
|
||||
|
||||
# Удаление узла
|
||||
# Обратите внимание, как AVL-дерево сохраняет баланс после удаления узла
|
||||
test_remove(avl_tree, 8) # Удаление узла степени 0
|
||||
test_remove(avl_tree, 5) # Удаление узла степени 1
|
||||
test_remove(avl_tree, 4) # Удаление узла степени 2
|
||||
|
||||
result_node = avl_tree.search(7)
|
||||
print("\nНайденный объект узла = {}, значение узла = {}".format(result_node, result_node.val))
|
||||
146
ru/codes/python/chapter_tree/binary_search_tree.py
Normal file
146
ru/codes/python/chapter_tree/binary_search_tree.py
Normal file
@@ -0,0 +1,146 @@
|
||||
"""
|
||||
File: binary_search_tree.py
|
||||
Created Time: 2022-12-20
|
||||
Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree
|
||||
|
||||
|
||||
class BinarySearchTree:
|
||||
"""Двоичное дерево поиска"""
|
||||
|
||||
def __init__(self):
|
||||
"""Конструктор"""
|
||||
# Инициализировать пустое дерево
|
||||
self._root = None
|
||||
|
||||
def get_root(self) -> TreeNode | None:
|
||||
"""Получить корневой узел двоичного дерева"""
|
||||
return self._root
|
||||
|
||||
def search(self, num: int) -> TreeNode | None:
|
||||
"""Поиск узла"""
|
||||
cur = self._root
|
||||
# Искать в цикле и выйти после прохода за листовой узел
|
||||
while cur is not None:
|
||||
# Целевой узел находится в правом поддереве cur
|
||||
if cur.val < num:
|
||||
cur = cur.right
|
||||
# Целевой узел находится в левом поддереве cur
|
||||
elif cur.val > num:
|
||||
cur = cur.left
|
||||
# Найти целевой узел и выйти из цикла
|
||||
else:
|
||||
break
|
||||
return cur
|
||||
|
||||
def insert(self, num: int):
|
||||
"""Вставка узла"""
|
||||
# Если дерево пусто, инициализировать корневой узел
|
||||
if self._root is None:
|
||||
self._root = TreeNode(num)
|
||||
return
|
||||
# Искать в цикле и выйти после прохода за листовой узел
|
||||
cur, pre = self._root, None
|
||||
while cur is not None:
|
||||
# Найти повторяющийся узел и сразу вернуть
|
||||
if cur.val == num:
|
||||
return
|
||||
pre = cur
|
||||
# Позиция вставки находится в правом поддереве cur
|
||||
if cur.val < num:
|
||||
cur = cur.right
|
||||
# Позиция вставки находится в левом поддереве cur
|
||||
else:
|
||||
cur = cur.left
|
||||
# Вставка узла
|
||||
node = TreeNode(num)
|
||||
if pre.val < num:
|
||||
pre.right = node
|
||||
else:
|
||||
pre.left = node
|
||||
|
||||
def remove(self, num: int):
|
||||
"""Удаление узла"""
|
||||
# Если дерево пусто, сразу вернуть
|
||||
if self._root is None:
|
||||
return
|
||||
# Искать в цикле и выйти после прохода за листовой узел
|
||||
cur, pre = self._root, None
|
||||
while cur is not None:
|
||||
# Найти узел для удаления и выйти из цикла
|
||||
if cur.val == num:
|
||||
break
|
||||
pre = cur
|
||||
# Узел для удаления находится в правом поддереве cur
|
||||
if cur.val < num:
|
||||
cur = cur.right
|
||||
# Узел для удаления находится в левом поддереве cur
|
||||
else:
|
||||
cur = cur.left
|
||||
# Если узел для удаления отсутствует, сразу вернуть
|
||||
if cur is None:
|
||||
return
|
||||
|
||||
# Число дочерних узлов = 0 или 1
|
||||
if cur.left is None or cur.right is None:
|
||||
# Когда число дочерних узлов = 0 / 1, child = null / этот дочерний узел
|
||||
child = cur.left or cur.right
|
||||
# Удалить узел cur
|
||||
if cur != self._root:
|
||||
if pre.left == cur:
|
||||
pre.left = child
|
||||
else:
|
||||
pre.right = child
|
||||
else:
|
||||
# Если удаляемый узел является корнем, заново назначить корневой узел
|
||||
self._root = child
|
||||
# Число дочерних узлов = 2
|
||||
else:
|
||||
# Получить следующий узел после cur в симметричном обходе
|
||||
tmp: TreeNode = cur.right
|
||||
while tmp.left is not None:
|
||||
tmp = tmp.left
|
||||
# Рекурсивно удалить узел tmp
|
||||
self.remove(tmp.val)
|
||||
# Перезаписать cur значением tmp
|
||||
cur.val = tmp.val
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация двоичного дерева поиска
|
||||
bst = BinarySearchTree()
|
||||
nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]
|
||||
# Обратите внимание: разные порядки вставки порождают разные двоичные деревья; данная последовательность может построить совершенное двоичное дерево
|
||||
for num in nums:
|
||||
bst.insert(num)
|
||||
print("\nИсходное двоичное дерево\n")
|
||||
print_tree(bst.get_root())
|
||||
|
||||
# Поиск узла
|
||||
node = bst.search(7)
|
||||
print("\nНайденный объект узла = {}, значение узла = {}".format(node, node.val))
|
||||
|
||||
# Вставка узла
|
||||
bst.insert(16)
|
||||
print("\nПосле вставки узла 16 двоичное дерево имеет вид\n")
|
||||
print_tree(bst.get_root())
|
||||
|
||||
# Удаление узла
|
||||
bst.remove(1)
|
||||
print("\nПосле удаления узла 1 двоичное дерево имеет вид\n")
|
||||
print_tree(bst.get_root())
|
||||
|
||||
bst.remove(2)
|
||||
print("\nПосле удаления узла 2 двоичное дерево имеет вид\n")
|
||||
print_tree(bst.get_root())
|
||||
|
||||
bst.remove(4)
|
||||
print("\nПосле удаления узла 4 двоичное дерево имеет вид\n")
|
||||
print_tree(bst.get_root())
|
||||
41
ru/codes/python/chapter_tree/binary_tree.py
Normal file
41
ru/codes/python/chapter_tree/binary_tree.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
File: binary_tree.py
|
||||
Created Time: 2022-12-20
|
||||
Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализация двоичного дерева
|
||||
# Инициализация узлов
|
||||
n1 = TreeNode(val=1)
|
||||
n2 = TreeNode(val=2)
|
||||
n3 = TreeNode(val=3)
|
||||
n4 = TreeNode(val=4)
|
||||
n5 = TreeNode(val=5)
|
||||
# Построить связи между узлами (указатели)
|
||||
n1.left = n2
|
||||
n1.right = n3
|
||||
n2.left = n4
|
||||
n2.right = n5
|
||||
print("\nИнициализация двоичного дерева\n")
|
||||
print_tree(n1)
|
||||
|
||||
# Вставка и удаление узлов
|
||||
P = TreeNode(0)
|
||||
# Вставить узел P между n1 -> n2
|
||||
n1.left = P
|
||||
P.left = n2
|
||||
print("\nПосле вставки узла P\n")
|
||||
print_tree(n1)
|
||||
# Удаление узла
|
||||
n1.left = n2
|
||||
print("\nПосле удаления узла P\n")
|
||||
print_tree(n1)
|
||||
42
ru/codes/python/chapter_tree/binary_tree_bfs.py
Normal file
42
ru/codes/python/chapter_tree/binary_tree_bfs.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
File: binary_tree_bfs.py
|
||||
Created Time: 2022-12-20
|
||||
Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, list_to_tree, print_tree
|
||||
from collections import deque
|
||||
|
||||
|
||||
def level_order(root: TreeNode | None) -> list[int]:
|
||||
"""Обход в ширину"""
|
||||
# Инициализировать очередь и добавить корневой узел
|
||||
queue: deque[TreeNode] = deque()
|
||||
queue.append(root)
|
||||
# Инициализировать список для хранения последовательности обхода
|
||||
res = []
|
||||
while queue:
|
||||
node: TreeNode = queue.popleft() # Извлечение из очереди
|
||||
res.append(node.val) # Сохранить значение узла
|
||||
if node.left is not None:
|
||||
queue.append(node.left) # Поместить левый дочерний узел в очередь
|
||||
if node.right is not None:
|
||||
queue.append(node.right) # Поместить правый дочерний узел в очередь
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализировать двоичное дерево
|
||||
# Здесь используется функция, напрямую строящая двоичное дерево из массива
|
||||
root: TreeNode = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7])
|
||||
print("\nИнициализация двоичного дерева\n")
|
||||
print_tree(root)
|
||||
|
||||
# Обход в ширину
|
||||
res: list[int] = level_order(root)
|
||||
print("\nПоследовательность печати узлов при обходе в ширину = ", res)
|
||||
65
ru/codes/python/chapter_tree/binary_tree_dfs.py
Normal file
65
ru/codes/python/chapter_tree/binary_tree_dfs.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
File: binary_tree_dfs.py
|
||||
Created Time: 2022-12-20
|
||||
Author: a16su (lpluls001@gmail.com)
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, list_to_tree, print_tree
|
||||
|
||||
|
||||
def pre_order(root: TreeNode | None):
|
||||
"""Предварительный обход"""
|
||||
if root is None:
|
||||
return
|
||||
# Порядок обхода: корень -> левое поддерево -> правое поддерево
|
||||
res.append(root.val)
|
||||
pre_order(root=root.left)
|
||||
pre_order(root=root.right)
|
||||
|
||||
|
||||
def in_order(root: TreeNode | None):
|
||||
"""Симметричный обход"""
|
||||
if root is None:
|
||||
return
|
||||
# Порядок обхода: левое поддерево -> корень -> правое поддерево
|
||||
in_order(root=root.left)
|
||||
res.append(root.val)
|
||||
in_order(root=root.right)
|
||||
|
||||
|
||||
def post_order(root: TreeNode | None):
|
||||
"""Обратный обход"""
|
||||
if root is None:
|
||||
return
|
||||
# Порядок обхода: левое поддерево -> правое поддерево -> корень
|
||||
post_order(root=root.left)
|
||||
post_order(root=root.right)
|
||||
res.append(root.val)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
if __name__ == "__main__":
|
||||
# Инициализировать двоичное дерево
|
||||
# Здесь используется функция, напрямую строящая двоичное дерево из массива
|
||||
root = list_to_tree(arr=[1, 2, 3, 4, 5, 6, 7])
|
||||
print("\nИнициализация двоичного дерева\n")
|
||||
print_tree(root)
|
||||
|
||||
# Предварительный обход
|
||||
res = []
|
||||
pre_order(root)
|
||||
print("\nПоследовательность печати узлов при предварительном обходе = ", res)
|
||||
|
||||
# Симметричный обход
|
||||
res.clear()
|
||||
in_order(root)
|
||||
print("\nПоследовательность печати узлов при симметричном обходе = ", res)
|
||||
|
||||
# Обратный обход
|
||||
res.clear()
|
||||
post_order(root)
|
||||
print("\nПоследовательность печати узлов при обратном обходе = ", res)
|
||||
19
ru/codes/python/modules/__init__.py
Normal file
19
ru/codes/python/modules/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Follow the PEP 585 - Type Hinting Generics In Standard Collections
|
||||
# https://peps.python.org/pep-0585/
|
||||
from __future__ import annotations
|
||||
|
||||
# Import common libs here to simplify the code by `from module import *`
|
||||
from .list_node import (
|
||||
ListNode,
|
||||
list_to_linked_list,
|
||||
linked_list_to_list,
|
||||
)
|
||||
from .tree_node import TreeNode, list_to_tree, tree_to_list
|
||||
from .vertex import Vertex, vals_to_vets, vets_to_vals
|
||||
from .print_util import (
|
||||
print_matrix,
|
||||
print_linked_list,
|
||||
print_tree,
|
||||
print_dict,
|
||||
print_heap,
|
||||
)
|
||||
32
ru/codes/python/modules/list_node.py
Normal file
32
ru/codes/python/modules/list_node.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
File: list_node.py
|
||||
Created Time: 2021-12-11
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
|
||||
class ListNode:
|
||||
"""Класс узла связного списка"""
|
||||
|
||||
def __init__(self, val: int):
|
||||
self.val: int = val # Значение узла
|
||||
self.next: ListNode | None = None # Ссылка на узел-преемник
|
||||
|
||||
|
||||
def list_to_linked_list(arr: list[int]) -> ListNode | None:
|
||||
"""Десериализовать список в связный список"""
|
||||
dum = head = ListNode(0)
|
||||
for a in arr:
|
||||
node = ListNode(a)
|
||||
head.next = node
|
||||
head = head.next
|
||||
return dum.next
|
||||
|
||||
|
||||
def linked_list_to_list(head: ListNode | None) -> list[int]:
|
||||
"""Сериализовать связный список в список"""
|
||||
arr: list[int] = []
|
||||
while head:
|
||||
arr.append(head.val)
|
||||
head = head.next
|
||||
return arr
|
||||
81
ru/codes/python/modules/print_util.py
Normal file
81
ru/codes/python/modules/print_util.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
File: print_util.py
|
||||
Created Time: 2021-12-11
|
||||
Author: krahets (krahets@163.com), msk397 (machangxinq@gmail.com)
|
||||
"""
|
||||
|
||||
from .tree_node import TreeNode, list_to_tree
|
||||
from .list_node import ListNode, linked_list_to_list
|
||||
|
||||
|
||||
def print_matrix(mat: list[list[int]]):
|
||||
"""Вывести матрицу"""
|
||||
s = []
|
||||
for arr in mat:
|
||||
s.append(" " + str(arr))
|
||||
print("[\n" + ",\n".join(s) + "\n]")
|
||||
|
||||
|
||||
def print_linked_list(head: ListNode | None):
|
||||
"""Вывести связный список"""
|
||||
arr: list[int] = linked_list_to_list(head)
|
||||
print(" -> ".join([str(a) for a in arr]))
|
||||
|
||||
|
||||
class Trunk:
|
||||
def __init__(self, prev, string: str | None = None):
|
||||
self.prev = prev
|
||||
self.str = string
|
||||
|
||||
|
||||
def show_trunks(p: Trunk | None):
|
||||
if p is None:
|
||||
return
|
||||
show_trunks(p.prev)
|
||||
print(p.str, end="")
|
||||
|
||||
|
||||
def print_tree(
|
||||
root: TreeNode | None, prev: Trunk | None = None, is_right: bool = False
|
||||
):
|
||||
"""
|
||||
Вывести двоичное дерево
|
||||
Этот вывод дерева заимствован из TECHIE DELIGHT
|
||||
https://www.techiedelight.com/c-program-print-binary-tree/
|
||||
"""
|
||||
if root is None:
|
||||
return
|
||||
|
||||
prev_str = " "
|
||||
trunk = Trunk(prev, prev_str)
|
||||
print_tree(root.right, trunk, True)
|
||||
|
||||
if prev is None:
|
||||
trunk.str = "———"
|
||||
elif is_right:
|
||||
trunk.str = "/———"
|
||||
prev_str = " |"
|
||||
else:
|
||||
trunk.str = "\———"
|
||||
prev.str = prev_str
|
||||
|
||||
show_trunks(trunk)
|
||||
print(" " + str(root.val))
|
||||
if prev:
|
||||
prev.str = prev_str
|
||||
trunk.str = " |"
|
||||
print_tree(root.left, trunk, False)
|
||||
|
||||
|
||||
def print_dict(hmap: dict):
|
||||
"""Вывести словарь"""
|
||||
for key, value in hmap.items():
|
||||
print(key, "->", value)
|
||||
|
||||
|
||||
def print_heap(heap: list[int]):
|
||||
"""Вывести кучу"""
|
||||
print("Массивное представление кучи:", heap)
|
||||
print("Древовидное представление кучи:")
|
||||
root: TreeNode | None = list_to_tree(heap)
|
||||
print_tree(root)
|
||||
69
ru/codes/python/modules/tree_node.py
Normal file
69
ru/codes/python/modules/tree_node.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
File: tree_node.py
|
||||
Created Time: 2021-12-11
|
||||
Author: krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
from collections import deque
|
||||
|
||||
|
||||
class TreeNode:
|
||||
"""Класс узла двоичного дерева"""
|
||||
|
||||
def __init__(self, val: int = 0):
|
||||
self.val: int = val # Значение узла
|
||||
self.height: int = 0 # Высота узла
|
||||
self.left: TreeNode | None = None # Ссылка на левый дочерний узел
|
||||
self.right: TreeNode | None = None # Ссылка на правый дочерний узел
|
||||
|
||||
# Правила кодирования сериализации см.:
|
||||
# https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
|
||||
# Массивное представление двоичного дерева:
|
||||
# [1, 2, 3, 4, None, 6, 7, 8, 9, None, None, 12, None, None, 15]
|
||||
# Связное представление двоичного дерева:
|
||||
# /——— 15
|
||||
# /——— 7
|
||||
# /——— 3
|
||||
# | \——— 6
|
||||
# | \——— 12
|
||||
# ——— 1
|
||||
# \——— 2
|
||||
# | /——— 9
|
||||
# \——— 4
|
||||
# \——— 8
|
||||
|
||||
|
||||
def list_to_tree_dfs(arr: list[int], i: int) -> TreeNode | None:
|
||||
"""Десериализовать список в двоичное дерево: рекурсия"""
|
||||
# Если индекс выходит за длину массива или соответствующий элемент равен None, вернуть None
|
||||
if i < 0 or i >= len(arr) or arr[i] is None:
|
||||
return None
|
||||
# Построить текущий узел
|
||||
root = TreeNode(arr[i])
|
||||
# Рекурсивно построить левое и правое поддеревья
|
||||
root.left = list_to_tree_dfs(arr, 2 * i + 1)
|
||||
root.right = list_to_tree_dfs(arr, 2 * i + 2)
|
||||
return root
|
||||
|
||||
|
||||
def list_to_tree(arr: list[int]) -> TreeNode | None:
|
||||
"""Десериализовать список в двоичное дерево"""
|
||||
return list_to_tree_dfs(arr, 0)
|
||||
|
||||
|
||||
def tree_to_list_dfs(root: TreeNode, i: int, res: list[int]) -> list[int]:
|
||||
"""Сериализовать двоичное дерево в список: рекурсия"""
|
||||
if root is None:
|
||||
return
|
||||
if i >= len(res):
|
||||
res += [None] * (i - len(res) + 1)
|
||||
res[i] = root.val
|
||||
tree_to_list_dfs(root.left, 2 * i + 1, res)
|
||||
tree_to_list_dfs(root.right, 2 * i + 2, res)
|
||||
|
||||
|
||||
def tree_to_list(root: TreeNode | None) -> list[int]:
|
||||
"""Сериализовать двоичное дерево в список"""
|
||||
res = []
|
||||
tree_to_list_dfs(root, 0, res)
|
||||
return res
|
||||
20
ru/codes/python/modules/vertex.py
Normal file
20
ru/codes/python/modules/vertex.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# File: vertex.py
|
||||
# Created Time: 2023-02-23
|
||||
# Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
class Vertex:
|
||||
"""Класс вершины"""
|
||||
|
||||
def __init__(self, val: int):
|
||||
self.val = val
|
||||
|
||||
|
||||
def vals_to_vets(vals: list[int]) -> list["Vertex"]:
|
||||
"""На вход подается список значений vals, на выходе возвращается список вершин vets"""
|
||||
return [Vertex(val) for val in vals]
|
||||
|
||||
|
||||
def vets_to_vals(vets: list["Vertex"]) -> list[int]:
|
||||
"""На вход подается список вершин vets, на выходе возвращается список значений vals"""
|
||||
return [vet.val for vet in vets]
|
||||
33
ru/codes/python/test_all.py
Normal file
33
ru/codes/python/test_all.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
import glob
|
||||
import subprocess
|
||||
|
||||
env = os.environ.copy()
|
||||
env["PYTHONIOENCODING"] = "utf-8"
|
||||
|
||||
if __name__ == "__main__":
|
||||
# find source code files
|
||||
src_paths = sorted(glob.glob("chapter_*/*.py"))
|
||||
errors = []
|
||||
|
||||
# run python code
|
||||
for src_path in src_paths:
|
||||
process = subprocess.Popen(
|
||||
["python", src_path],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
env=env,
|
||||
encoding='utf-8'
|
||||
)
|
||||
# Wait for the process to complete, and get the output and error messages
|
||||
stdout, stderr = process.communicate()
|
||||
# Check the exit status
|
||||
exit_status = process.returncode
|
||||
if exit_status != 0:
|
||||
errors.append(stderr)
|
||||
|
||||
print(f"Tested {len(src_paths)} files")
|
||||
print(f"Found exception in {len(errors)} files")
|
||||
if len(errors) > 0:
|
||||
raise RuntimeError("\n\n".join(errors))
|
||||
Reference in New Issue
Block a user