mirror of
https://github.com/krahets/hello-algo.git
synced 2024-12-24 03:56:28 +08:00
1. Add the building util of Python
for the markdown docs. 2. Update the deploy.sh
This commit is contained in:
parent
64f251f933
commit
ea901af217
28 changed files with 292 additions and 933 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -7,9 +7,6 @@
|
|||
hello-algo.iml
|
||||
|
||||
# mkdocs files
|
||||
site/
|
||||
.cache/
|
||||
scripts/
|
||||
docs/overrides/
|
||||
|
||||
src/
|
||||
|
|
|
@ -6,7 +6,7 @@ Author: msk397 (machangxinq@gmail.com)
|
|||
|
||||
""" 键值对 int->String """
|
||||
class Entry:
|
||||
def __init__(self, key: int, val: str):
|
||||
def __init__(self, key, val):
|
||||
self.key = key
|
||||
self.val = val
|
||||
|
||||
|
|
|
@ -54,11 +54,9 @@ class BinarySearchTree:
|
|||
# 若树为空,直接提前返回
|
||||
if root is None:
|
||||
return None
|
||||
|
||||
cur = root
|
||||
pre = None
|
||||
|
||||
|
||||
# 循环查找,越过叶结点后跳出
|
||||
cur, pre = root, None
|
||||
while cur is not None:
|
||||
# 找到重复结点,直接返回
|
||||
if cur.val == num:
|
||||
|
@ -86,10 +84,8 @@ class BinarySearchTree:
|
|||
if root is None:
|
||||
return None
|
||||
|
||||
cur = root
|
||||
pre = None
|
||||
|
||||
# 循环查找,越过叶结点后跳出
|
||||
cur, pre = root, None
|
||||
while cur is not None:
|
||||
# 找到待删除结点,跳出循环
|
||||
if cur.val == num:
|
||||
|
@ -99,7 +95,6 @@ class BinarySearchTree:
|
|||
cur = cur.right
|
||||
else: # 待删除结点在 cur 的左子树中
|
||||
cur = cur.left
|
||||
|
||||
# 若无待删除结点,则直接返回
|
||||
if cur is None:
|
||||
return None
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
rm -rf ./site
|
||||
mkdocs build --clean
|
||||
cd site
|
||||
git init
|
||||
git add -A
|
||||
git commit -m "deploy"
|
||||
git push -f git@github.com:krahets/hello-algo.git main:gh-pages
|
||||
cd -
|
51
docs/chapter_array_and_linkedlist/array.md
Normal file → Executable file
51
docs/chapter_array_and_linkedlist/array.md
Normal file → Executable file
|
@ -143,13 +143,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
|||
=== "Python"
|
||||
|
||||
```python title="array.py"
|
||||
""" 随机访问元素 """
|
||||
def random_access(nums):
|
||||
# 在区间 [0, len(nums)-1] 中随机抽取一个数字
|
||||
random_index = random.randint(0, len(nums) - 1)
|
||||
# 获取并返回随机元素
|
||||
random_num = nums[random_index]
|
||||
return random_num
|
||||
[class]{}-[func]{random_access}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -279,17 +273,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
|||
=== "Python"
|
||||
|
||||
```python title="array.py"
|
||||
""" 扩展数组长度 """
|
||||
# 请注意,Python 的 list 是动态数组,可以直接扩展
|
||||
# 为了方便学习,本函数将 list 看作是长度不可变的数组
|
||||
def extend(nums, enlarge):
|
||||
# 初始化一个扩展长度后的数组
|
||||
res = [0] * (len(nums) + enlarge)
|
||||
# 将原数组中的所有元素复制到新数组
|
||||
for i in range(len(nums)):
|
||||
res[i] = nums[i]
|
||||
# 返回扩展后的新数组
|
||||
return res
|
||||
[class]{}-[func]{extend}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -452,19 +436,9 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
|||
=== "Python"
|
||||
|
||||
```python title="array.py"
|
||||
""" 在数组的索引 index 处插入元素 num """
|
||||
def insert(nums, num, index):
|
||||
# 把索引 index 以及之后的所有元素向后移动一位
|
||||
for i in range(len(nums) - 1, index, -1):
|
||||
nums[i] = nums[i - 1]
|
||||
# 将 num 赋给 index 处元素
|
||||
nums[index] = num
|
||||
[class]{}-[func]{insert}
|
||||
|
||||
""" 删除索引 index 处元素 """
|
||||
def remove(nums, index):
|
||||
# 把索引 index 之后的所有元素向前移动一位
|
||||
for i in range(index, len(nums) - 1):
|
||||
nums[i] = nums[i + 1]
|
||||
[class]{}-[func]{remove}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -648,15 +622,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
|||
=== "Python"
|
||||
|
||||
```python title="array.py"
|
||||
""" 遍历数组 """
|
||||
def traverse(nums):
|
||||
count = 0
|
||||
# 通过索引遍历数组
|
||||
for i in range(len(nums)):
|
||||
count += 1
|
||||
# 直接遍历数组
|
||||
for num in nums:
|
||||
count += 1
|
||||
[class]{}-[func]{traverse}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -803,12 +769,7 @@ elementAddr = firtstElementAddr + elementLength * elementIndex
|
|||
=== "Python"
|
||||
|
||||
```python title="array.py"
|
||||
""" 在数组中查找指定元素 """
|
||||
def find(nums, target):
|
||||
for i in range(len(nums)):
|
||||
if nums[i] == target:
|
||||
return i
|
||||
return -1
|
||||
[class]{}-[func]{find}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
33
docs/chapter_array_and_linkedlist/linked_list.md
Normal file → Executable file
33
docs/chapter_array_and_linkedlist/linked_list.md
Normal file → Executable file
|
@ -369,20 +369,9 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="linked_list.py"
|
||||
""" 在链表的结点 n0 之后插入结点 P """
|
||||
def insert(n0, P):
|
||||
n1 = n0.next
|
||||
n0.next = P
|
||||
P.next = n1
|
||||
[class]{}-[func]{insert}
|
||||
|
||||
""" 删除链表的结点 n0 之后的首个结点 """
|
||||
def remove(n0):
|
||||
if not n0.next:
|
||||
return
|
||||
# n0 -> P -> n1
|
||||
P = n0.next
|
||||
n1 = P.next
|
||||
n0.next = n1
|
||||
[class]{}-[func]{remove}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -557,13 +546,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="linked_list.py"
|
||||
""" 访问链表中索引为 index 的结点 """
|
||||
def access(head, index):
|
||||
for _ in range(index):
|
||||
if not head:
|
||||
return None
|
||||
head = head.next
|
||||
return head
|
||||
[class]{}-[func]{access}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -704,15 +687,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="linked_list.py"
|
||||
""" 在链表中查找值为 target 的首个结点 """
|
||||
def find(head, target):
|
||||
index = 0
|
||||
while head:
|
||||
if head.val == target:
|
||||
return index
|
||||
head = head.next
|
||||
index += 1
|
||||
return -1
|
||||
[class]{}-[func]{find}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
63
docs/chapter_array_and_linkedlist/list.md
Normal file → Executable file
63
docs/chapter_array_and_linkedlist/list.md
Normal file → Executable file
|
@ -912,68 +912,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="my_list.py"
|
||||
""" 列表类简易实现 """
|
||||
class MyList:
|
||||
""" 构造函数 """
|
||||
def __init__(self):
|
||||
self.__capacity = 10 # 列表容量
|
||||
self.__nums = [0] * self.__capacity # 数组(存储列表元素)
|
||||
self.__size = 0 # 列表长度(即当前元素数量)
|
||||
self.__extend_ratio = 2 # 每次列表扩容的倍数
|
||||
|
||||
""" 获取列表长度(即当前元素数量) """
|
||||
def size(self):
|
||||
return self.__size
|
||||
|
||||
""" 获取列表容量 """
|
||||
def capacity(self):
|
||||
return self.__capacity
|
||||
|
||||
""" 访问元素 """
|
||||
def get(self, index):
|
||||
# 索引如果越界则抛出异常,下同
|
||||
assert index >= 0 and index < self.__size, "索引越界"
|
||||
return self.__nums[index]
|
||||
|
||||
""" 更新元素 """
|
||||
def set(self, num, index):
|
||||
assert index >= 0 and index < self.__size, "索引越界"
|
||||
self.__nums[index] = num
|
||||
|
||||
""" 中间插入(尾部添加)元素 """
|
||||
def add(self, num, index=-1):
|
||||
assert index >= 0 and index < self.__size, "索引越界"
|
||||
# 若不指定索引 index ,则向数组尾部添加元素
|
||||
if index == -1:
|
||||
index = self.__size
|
||||
# 元素数量超出容量时,触发扩容机制
|
||||
if self.__size == self.capacity():
|
||||
self.extend_capacity()
|
||||
# 索引 i 以及之后的元素都向后移动一位
|
||||
for j in range(self.__size - 1, index - 1, -1):
|
||||
self.__nums[j + 1] = self.__nums[j]
|
||||
self.__nums[index] = num
|
||||
# 更新元素数量
|
||||
self.__size += 1
|
||||
|
||||
""" 删除元素 """
|
||||
def remove(self, index):
|
||||
assert index >= 0 and index < self.__size, "索引越界"
|
||||
num = self.nums[index]
|
||||
# 索引 i 之后的元素都向前移动一位
|
||||
for j in range(index, self.__size - 1):
|
||||
self.__nums[j] = self.__nums[j + 1]
|
||||
# 更新元素数量
|
||||
self.__size -= 1
|
||||
# 返回被删除元素
|
||||
return num
|
||||
|
||||
""" 列表扩容 """
|
||||
def extend_capacity(self):
|
||||
# 新建一个长度为 self.__size 的数组,并将原数组拷贝到新数组
|
||||
self.__nums = self.__nums + [0] * self.capacity() * (self.__extend_ratio - 1)
|
||||
# 更新列表容量
|
||||
self.__capacity = len(self.__nums)
|
||||
[class]{MyList}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
48
docs/chapter_computational_complexity/space_complexity.md
Normal file → Executable file
48
docs/chapter_computational_complexity/space_complexity.md
Normal file → Executable file
|
@ -624,18 +624,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="space_complexity.py"
|
||||
""" 常数阶 """
|
||||
def constant(n):
|
||||
# 常量、变量、对象占用 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()
|
||||
[class]{}-[func]{constant}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -829,14 +818,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="space_complexity.py"
|
||||
""" 线性阶 """
|
||||
def linear(n):
|
||||
# 长度为 n 的列表占用 O(n) 空间
|
||||
nums = [0] * n
|
||||
# 长度为 n 的哈希表占用 O(n) 空间
|
||||
mapp = {}
|
||||
for i in range(n):
|
||||
mapp[i] = str(i)
|
||||
[class]{}-[func]{linear}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -996,11 +978,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="space_complexity.py"
|
||||
""" 线性阶(递归实现) """
|
||||
def linear_recur(n):
|
||||
print("递归 n =", n)
|
||||
if n == 1: return
|
||||
linear_recur(n - 1)
|
||||
[class]{}-[func]{linear_recur}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1127,10 +1105,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="space_complexity.py"
|
||||
""" 平方阶 """
|
||||
def quadratic(n):
|
||||
# 二维列表占用 O(n^2) 空间
|
||||
num_matrix = [[0] * n for _ in range(n)]
|
||||
[class]{}-[func]{quadratic}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1272,12 +1247,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="space_complexity.py"
|
||||
""" 平方阶(递归实现) """
|
||||
def quadratic_recur(n):
|
||||
if n <= 0: return 0
|
||||
# 数组 nums 长度为 n, n-1, ..., 2, 1
|
||||
nums = [0] * n
|
||||
return quadratic_recur(n - 1)
|
||||
[class]{}-[func]{quadratic_recur}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1400,13 +1370,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="space_complexity.py"
|
||||
""" 指数阶(建立满二叉树) """
|
||||
def build_tree(n):
|
||||
if n == 0: return None
|
||||
root = TreeNode(0)
|
||||
root.left = build_tree(n - 1)
|
||||
root.right = build_tree(n - 1)
|
||||
return root
|
||||
[class]{}-[func]{build_tree}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
20
docs/chapter_computational_complexity/space_time_tradeoff.md
Normal file → Executable file
20
docs/chapter_computational_complexity/space_time_tradeoff.md
Normal file → Executable file
|
@ -70,14 +70,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="leetcode_two_sum.py"
|
||||
class SolutionBruteForce:
|
||||
def twoSum(self, nums: List[int], target: int) -> List[int]:
|
||||
# 两层循环,时间复杂度 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 []
|
||||
[class]{SolutionBruteForce}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -247,16 +240,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="leetcode_two_sum.py"
|
||||
class SolutionHashMap:
|
||||
def twoSum(self, nums: List[int], target: int) -> List[int]:
|
||||
# 辅助哈希表,空间复杂度 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 []
|
||||
[class]{SolutionHashMap}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
111
docs/chapter_computational_complexity/time_complexity.md
Normal file → Executable file
111
docs/chapter_computational_complexity/time_complexity.md
Normal file → Executable file
|
@ -821,13 +821,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 常数阶 """
|
||||
def constant(n):
|
||||
count = 0
|
||||
size = 100000
|
||||
for _ in range(size):
|
||||
count += 1
|
||||
return count
|
||||
[class]{}-[func]{constant}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -958,12 +952,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 线性阶 """
|
||||
def linear(n):
|
||||
count = 0
|
||||
for _ in range(n):
|
||||
count += 1
|
||||
return count
|
||||
[class]{}-[func]{linear}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1091,13 +1080,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 线性阶(遍历数组)"""
|
||||
def array_traversal(nums):
|
||||
count = 0
|
||||
# 循环次数与数组长度成正比
|
||||
for num in nums:
|
||||
count += 1
|
||||
return count
|
||||
[class]{}-[func]{array_traversal}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1239,14 +1222,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 平方阶 """
|
||||
def quadratic(n):
|
||||
count = 0
|
||||
# 循环次数与数组长度成平方关系
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
count += 1
|
||||
return count
|
||||
[class]{}-[func]{quadratic}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1425,20 +1401,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 平方阶(冒泡排序)"""
|
||||
def bubble_sort(nums):
|
||||
count = 0 # 计数器
|
||||
# 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
for i in range(len(nums) - 1, 0, -1):
|
||||
# 内循环:冒泡操作
|
||||
for j in range(i):
|
||||
if nums[j] > nums[j + 1]:
|
||||
# 交换 nums[j] 与 nums[j + 1]
|
||||
tmp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = tmp
|
||||
count += 3 # 元素交换包含 3 个单元操作
|
||||
return count
|
||||
[class]{}-[func]{bubble_sort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1658,16 +1621,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 指数阶(循环实现)"""
|
||||
def exponential(n):
|
||||
count, base = 0, 1
|
||||
# cell 每轮一分为二,形成数列 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
|
||||
[class]{}-[func]{exponential}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1836,10 +1790,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 指数阶(递归实现)"""
|
||||
def exp_recur(n):
|
||||
if n == 1: return 1
|
||||
return exp_recur(n - 1) + exp_recur(n - 1) + 1
|
||||
[class]{}-[func]{exp_recur}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1957,13 +1908,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 对数阶(循环实现)"""
|
||||
def logarithmic(n):
|
||||
count = 0
|
||||
while n > 1:
|
||||
n = n / 2
|
||||
count += 1
|
||||
return count
|
||||
[class]{}-[func]{logarithmic}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -2099,10 +2044,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 对数阶(递归实现)"""
|
||||
def log_recur(n):
|
||||
if n <= 1: return 0
|
||||
return log_recur(n / 2) + 1
|
||||
[class]{}-[func]{log_recur}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -2220,14 +2162,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 线性对数阶 """
|
||||
def linear_log_recur(n):
|
||||
if n <= 1: return 1
|
||||
count = linear_log_recur(n // 2) + \
|
||||
linear_log_recur(n // 2)
|
||||
for _ in range(n):
|
||||
count += 1
|
||||
return count
|
||||
[class]{}-[func]{linear_log_recur}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -2387,14 +2322,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="time_complexity.py"
|
||||
""" 阶乘阶(递归实现)"""
|
||||
def factorial_recur(n):
|
||||
if n == 0: return 1
|
||||
count = 0
|
||||
# 从 1 个分裂出 n 个
|
||||
for _ in range(n):
|
||||
count += factorial_recur(n - 1)
|
||||
return count
|
||||
[class]{}-[func]{factorial_recur}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -2587,22 +2515,9 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="worst_best_time_complexity.py"
|
||||
""" 生成一个数组,元素为: 1, 2, ..., n ,顺序被打乱 """
|
||||
def random_numbers(n):
|
||||
# 生成数组 nums =: 1, 2, 3, ..., n
|
||||
nums = [i for i in range(1, n + 1)]
|
||||
# 随机打乱数组元素
|
||||
random.shuffle(nums)
|
||||
return nums
|
||||
[class]{}-[func]{random_numbers}
|
||||
|
||||
""" 查找数组 nums 中数字 1 所在索引 """
|
||||
def find_one(nums):
|
||||
for i in range(len(nums)):
|
||||
# 当元素 1 在数组头部时,达到最佳时间复杂度 O(1)
|
||||
# 当元素 1 在数组尾部时,达到最差时间复杂度 O(n)
|
||||
if nums[i] == 1:
|
||||
return i
|
||||
return -1
|
||||
[class]{}-[func]{find_one}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
37
docs/chapter_hashing/hash_map.md
Normal file → Executable file
37
docs/chapter_hashing/hash_map.md
Normal file → Executable file
|
@ -523,42 +523,9 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="array_hash_map.py"
|
||||
""" 键值对 int->String """
|
||||
class Entry:
|
||||
def __init__(self, key, val):
|
||||
self.key = key
|
||||
self.val = val
|
||||
[class]{Entry}-[func]{}
|
||||
|
||||
""" 基于数组简易实现的哈希表 """
|
||||
class ArrayHashMap:
|
||||
def __init__(self):
|
||||
# 初始化一个长度为 100 的桶(数组)
|
||||
self.bucket = [None] * 100
|
||||
|
||||
""" 哈希函数 """
|
||||
def hash_func(self, key):
|
||||
index = key % 100
|
||||
return index
|
||||
|
||||
""" 查询操作 """
|
||||
def get(self, key):
|
||||
index = self.hash_func(key)
|
||||
pair = self.bucket[index]
|
||||
if pair is None:
|
||||
return None
|
||||
return pair.val
|
||||
|
||||
""" 添加操作 """
|
||||
def put(self, key, val):
|
||||
pair = Entry(key, val)
|
||||
index = self.hash_func(key)
|
||||
self.bucket[index] = pair
|
||||
|
||||
""" 删除操作 """
|
||||
def remove(self, key):
|
||||
index = self.hash_func(key)
|
||||
# 置为 None ,代表删除
|
||||
self.bucket[index] = None
|
||||
[class]{ArrayHashMap}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
29
docs/chapter_searching/binary_search.md
Normal file → Executable file
29
docs/chapter_searching/binary_search.md
Normal file → Executable file
|
@ -98,19 +98,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="binary_search.py"
|
||||
""" 二分查找(双闭区间) """
|
||||
def binary_search(nums, target):
|
||||
# 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
|
||||
i, j = 0, len(nums) - 1
|
||||
while i <= j:
|
||||
m = (i + j) // 2 # 计算中点索引 m
|
||||
if nums[m] < target: # 此情况说明 target 在区间 [m+1, j] 中
|
||||
i = m + 1
|
||||
elif nums[m] > target: # 此情况说明 target 在区间 [i, m-1] 中
|
||||
j = m - 1
|
||||
else:
|
||||
return m # 找到目标元素,返回其索引
|
||||
return -1 # 未找到目标元素,返回 -1
|
||||
[class]{}-[func]{binary_search}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -291,20 +279,7 @@ $$
|
|||
=== "Python"
|
||||
|
||||
```python title="binary_search.py"
|
||||
""" 二分查找(左闭右开) """
|
||||
def binary_search1(nums, target):
|
||||
# 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
|
||||
i, j = 0, len(nums)
|
||||
# 循环,当搜索区间为空时跳出(当 i = j 时为空)
|
||||
while i < j:
|
||||
m = (i + j) // 2 # 计算中点索引 m
|
||||
if nums[m] < target: # 此情况说明 target 在区间 [m+1, j) 中
|
||||
i = m + 1
|
||||
elif nums[m] > target: # 此情况说明 target 在区间 [i, m) 中
|
||||
j = m
|
||||
else: # 找到目标元素,返回其索引
|
||||
return m
|
||||
return -1 # 未找到目标元素,返回 -1
|
||||
[class]{}-[func]{binary_search1}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
12
docs/chapter_searching/hashing_search.md
Normal file → Executable file
12
docs/chapter_searching/hashing_search.md
Normal file → Executable file
|
@ -43,11 +43,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="hashing_search.py"
|
||||
""" 哈希查找(数组) """
|
||||
def hashing_search_array(mapp, target):
|
||||
# 哈希表的 key: 目标元素,value: 索引
|
||||
# 若哈希表中无此 key ,返回 -1
|
||||
return mapp.get(target, -1)
|
||||
[class]{}-[func]{hashing_search_array}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -153,11 +149,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="hashing_search.py"
|
||||
""" 哈希查找(链表) """
|
||||
def hashing_search_linkedlist(mapp, target):
|
||||
# 哈希表的 key: 目标元素,value: 结点对象
|
||||
# 若哈希表中无此 key ,返回 -1
|
||||
return mapp.get(target, -1)
|
||||
[class]{}-[func]{hashing_search_linkedlist}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
17
docs/chapter_searching/linear_search.md
Normal file → Executable file
17
docs/chapter_searching/linear_search.md
Normal file → Executable file
|
@ -47,13 +47,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="linear_search.py"
|
||||
""" 线性查找(数组) """
|
||||
def linear_search_array(nums, target):
|
||||
# 遍历数组
|
||||
for i in range(len(nums)):
|
||||
if nums[i] == target: # 找到目标元素,返回其索引
|
||||
return i
|
||||
return -1 # 未找到目标元素,返回 -1
|
||||
[class]{}-[func]{linear_search_array}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -195,14 +189,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="linear_search.py"
|
||||
""" 线性查找(链表) """
|
||||
def linear_search_linkedlist(head, target):
|
||||
# 遍历链表
|
||||
while head:
|
||||
if head.val == target: # 找到目标结点,返回之
|
||||
return head
|
||||
head = head.next
|
||||
return None # 未找到目标结点,返回 None
|
||||
[class]{}-[func]{linear_search_linkedlist}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
33
docs/chapter_sorting/bubble_sort.md
Normal file → Executable file
33
docs/chapter_sorting/bubble_sort.md
Normal file → Executable file
|
@ -15,31 +15,24 @@ comments: true
|
|||
完成此次冒泡操作后,**数组最大元素已在正确位置,接下来只需排序剩余 $n - 1$ 个元素**。
|
||||
|
||||
=== "Step 1"
|
||||
|
||||
![bubble_operation_step1](bubble_sort.assets/bubble_operation_step1.png)
|
||||
|
||||
=== "Step 2"
|
||||
|
||||
![bubble_operation_step2](bubble_sort.assets/bubble_operation_step2.png)
|
||||
|
||||
=== "Step 3"
|
||||
|
||||
![bubble_operation_step3](bubble_sort.assets/bubble_operation_step3.png)
|
||||
|
||||
=== "Step 4"
|
||||
|
||||
![bubble_operation_step4](bubble_sort.assets/bubble_operation_step4.png)
|
||||
|
||||
=== "Step 5"
|
||||
|
||||
![bubble_operation_step5](bubble_sort.assets/bubble_operation_step5.png)
|
||||
|
||||
=== "Step 6"
|
||||
|
||||
![bubble_operation_step6](bubble_sort.assets/bubble_operation_step6.png)
|
||||
|
||||
=== "Step 7"
|
||||
|
||||
![bubble_operation_step7](bubble_sort.assets/bubble_operation_step7.png)
|
||||
|
||||
<p align="center"> Fig. 冒泡操作 </p>
|
||||
|
@ -96,16 +89,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="bubble_sort.py"
|
||||
""" 冒泡排序 """
|
||||
def bubble_sort(nums):
|
||||
n = len(nums)
|
||||
# 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
for i in range(n - 1, 0, -1):
|
||||
# 内循环:冒泡操作
|
||||
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]
|
||||
[class]{}-[func]{bubble_sort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -304,20 +288,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="bubble_sort.py"
|
||||
""" 冒泡排序(标志优化) """
|
||||
def bubble_sort_with_flag(nums):
|
||||
n = len(nums)
|
||||
# 外循环:待排序元素数量为 n-1, n-2, ..., 1
|
||||
for i in range(n - 1, 0, -1):
|
||||
flag = False # 初始化标志位
|
||||
# 内循环:冒泡操作
|
||||
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 # 此轮冒泡未交换任何元素,直接跳出
|
||||
[class]{}-[func]{bubble_sort_with_flag}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
12
docs/chapter_sorting/insertion_sort.md
Normal file → Executable file
12
docs/chapter_sorting/insertion_sort.md
Normal file → Executable file
|
@ -63,17 +63,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="insertion_sort.py"
|
||||
""" 插入排序 """
|
||||
def insertion_sort(nums):
|
||||
# 外循环:base = nums[1], nums[2], ..., nums[n-1]
|
||||
for i in range(1, len(nums)):
|
||||
base = nums[i]
|
||||
j = i - 1
|
||||
# 内循环:将 base 插入到左边的正确位置
|
||||
while j >= 0 and nums[j] > base:
|
||||
nums[j + 1] = nums[j] # 1. 将 nums[j] 向右移动一位
|
||||
j -= 1
|
||||
nums[j + 1] = base # 2. 将 base 赋值到正确位置
|
||||
[class]{}-[func]{insertion_sort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
41
docs/chapter_sorting/merge_sort.md
Normal file → Executable file
41
docs/chapter_sorting/merge_sort.md
Normal file → Executable file
|
@ -150,46 +150,9 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="merge_sort.py"
|
||||
"""
|
||||
合并左子数组和右子数组
|
||||
左子数组区间 [left, mid]
|
||||
右子数组区间 [mid + 1, right]
|
||||
"""
|
||||
def merge(nums, left, mid, right):
|
||||
# 初始化辅助数组 借助 copy模块
|
||||
tmp = nums[left:right + 1]
|
||||
# 左子数组的起始索引和结束索引
|
||||
left_start, left_end = left - left, mid - left
|
||||
# 右子数组的起始索引和结束索引
|
||||
right_start, right_end = mid + 1 - left, right - left
|
||||
# i, j 分别指向左子数组、右子数组的首元素
|
||||
i, j = left_start, right_start
|
||||
# 通过覆盖原数组 nums 来合并左子数组和右子数组
|
||||
for k in range(left, right + 1):
|
||||
# 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
|
||||
if i > left_end:
|
||||
nums[k] = tmp[j]
|
||||
j += 1
|
||||
# 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
|
||||
elif j > right_end or tmp[i] <= tmp[j]:
|
||||
nums[k] = tmp[i]
|
||||
i += 1
|
||||
# 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
|
||||
else:
|
||||
nums[k] = tmp[j]
|
||||
j += 1
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
""" 归并排序 """
|
||||
def merge_sort(nums, left, right):
|
||||
# 终止条件
|
||||
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)
|
||||
[class]{}-[func]{merge_sort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
59
docs/chapter_sorting/quick_sort.md
Normal file → Executable file
59
docs/chapter_sorting/quick_sort.md
Normal file → Executable file
|
@ -98,20 +98,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="quick_sort.py"
|
||||
""" 哨兵划分 """
|
||||
def partition(self, nums, left, right):
|
||||
# 以 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 # 返回基准数的索引
|
||||
[class]{QuickSort}-[func]{partition}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -316,16 +303,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="quick_sort.py"
|
||||
""" 快速排序 """
|
||||
def quick_sort(self, nums, left, right):
|
||||
# 子数组长度为 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]{QuickSort}-[func]{quick_sort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -509,24 +487,9 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="quick_sort.py"
|
||||
""" 选取三个元素的中位数 """
|
||||
def median_three(self, nums, left, mid, right):
|
||||
# 使用了异或操作来简化代码
|
||||
# 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1
|
||||
if (nums[left] < nums[mid]) ^ (nums[left] < nums[right]):
|
||||
return left
|
||||
elif (nums[mid] < nums[left]) ^ (nums[mid] > nums[right]):
|
||||
return mid
|
||||
return right
|
||||
[class]{QuickSortMedian}-[func]{median_three}
|
||||
|
||||
""" 哨兵划分(三数取中值) """
|
||||
def partition(self, nums, left, right):
|
||||
# 以 nums[left] 作为基准数
|
||||
med = self.median_three(nums, left, (left + right) // 2, right)
|
||||
# 将中位数交换至数组最左端
|
||||
nums[left], nums[med] = nums[med], nums[left]
|
||||
# 以 nums[left] 作为基准数
|
||||
# 下同省略...
|
||||
[class]{QuickSortMedian}-[func]{partition}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -721,19 +684,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="quick_sort.py"
|
||||
""" 快速排序(尾递归优化) """
|
||||
def quick_sort(self, nums, left, right):
|
||||
# 子数组长度为 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]
|
||||
[class]{QuickSortTailCall}-[func]{quick_sort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
86
docs/chapter_stack_and_queue/queue.md
Normal file → Executable file
86
docs/chapter_stack_and_queue/queue.md
Normal file → Executable file
|
@ -398,49 +398,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="linkedlist_queue.py"
|
||||
""" 基于链表实现的队列 """
|
||||
class LinkedListQueue:
|
||||
def __init__(self):
|
||||
self.__front = None # 头结点 front
|
||||
self.__rear = None # 尾结点 rear
|
||||
self.__size = 0
|
||||
|
||||
""" 获取队列的长度 """
|
||||
def size(self):
|
||||
return self.__size
|
||||
|
||||
""" 判断队列是否为空 """
|
||||
def is_empty(self):
|
||||
return not self.__front
|
||||
|
||||
""" 入队 """
|
||||
def push(self, num):
|
||||
# 尾结点后添加 num
|
||||
node = ListNode(num)
|
||||
# 如果队列为空,则令头、尾结点都指向该结点
|
||||
if self.__front is None:
|
||||
self.__front = node
|
||||
self.__rear = node
|
||||
# 如果队列不为空,则将该结点添加到尾结点后
|
||||
else:
|
||||
self.__rear.next = node
|
||||
self.__rear = node
|
||||
self.__size += 1
|
||||
|
||||
""" 出队 """
|
||||
def poll(self):
|
||||
num = self.peek()
|
||||
# 删除头结点
|
||||
self.__front = self.__front.next
|
||||
self.__size -= 1
|
||||
return num
|
||||
|
||||
""" 访问队首元素 """
|
||||
def peek(self):
|
||||
if self.size() == 0:
|
||||
print("队列为空")
|
||||
return False
|
||||
return self.__front.val
|
||||
[class]{LinkedListQueue}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -881,47 +839,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="array_queue.py"
|
||||
""" 基于环形数组实现的队列 """
|
||||
class ArrayQueue:
|
||||
def __init__(self, size):
|
||||
self.__nums = [0] * size # 用于存储队列元素的数组
|
||||
self.__front = 0 # 队首指针,指向队首元素
|
||||
self.__size = 0 # 队列长度
|
||||
|
||||
""" 获取队列的容量 """
|
||||
def capacity(self):
|
||||
return len(self.__nums)
|
||||
|
||||
""" 获取队列的长度 """
|
||||
def size(self):
|
||||
return self.__size
|
||||
|
||||
""" 判断队列是否为空 """
|
||||
def is_empty(self):
|
||||
return self.__size == 0
|
||||
|
||||
""" 入队 """
|
||||
def push(self, num):
|
||||
assert self.__size < self.capacity(), "队列已满"
|
||||
# 计算尾指针,指向队尾索引 + 1
|
||||
# 通过取余操作,实现 rear 越过数组尾部后回到头部
|
||||
rear = (self.__front + self.__size) % self.capacity()
|
||||
# 尾结点后添加 num
|
||||
self.__nums[rear] = num
|
||||
self.__size += 1
|
||||
|
||||
""" 出队 """
|
||||
def poll(self):
|
||||
num = self.peek()
|
||||
# 队首指针向后移动一位,若越过尾部则返回到数组头部
|
||||
self.__front = (self.__front + 1) % self.capacity()
|
||||
self.__size -= 1
|
||||
return num
|
||||
|
||||
""" 访问队首元素 """
|
||||
def peek(self):
|
||||
assert not self.is_empty(), "队列为空"
|
||||
return self.__nums[self.__front]
|
||||
[class]{ArrayQueue}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
61
docs/chapter_stack_and_queue/stack.md
Normal file → Executable file
61
docs/chapter_stack_and_queue/stack.md
Normal file → Executable file
|
@ -378,39 +378,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="linkedlist_stack.py"
|
||||
""" 基于链表实现的栈 """
|
||||
class LinkedListStack:
|
||||
def __init__(self):
|
||||
self.__peek = None
|
||||
self.__size = 0
|
||||
|
||||
""" 获取栈的长度 """
|
||||
def size(self):
|
||||
return self.__size
|
||||
|
||||
""" 判断栈是否为空 """
|
||||
def is_empty(self):
|
||||
return not self.__peek
|
||||
|
||||
""" 入栈 """
|
||||
def push(self, val):
|
||||
node = ListNode(val)
|
||||
node.next = self.__peek
|
||||
self.__peek = node
|
||||
self.__size += 1
|
||||
|
||||
""" 出栈 """
|
||||
def pop(self):
|
||||
num = self.peek()
|
||||
self.__peek = self.__peek.next
|
||||
self.__size -= 1
|
||||
return num
|
||||
|
||||
""" 访问栈顶元素 """
|
||||
def peek(self):
|
||||
# 判空处理
|
||||
if not self.__peek: return None
|
||||
return self.__peek.val
|
||||
[class]{LinkedListStack}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -785,32 +753,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="array_stack.py"
|
||||
""" 基于数组实现的栈 """
|
||||
class ArrayStack:
|
||||
def __init__(self):
|
||||
self.__stack = []
|
||||
|
||||
""" 获取栈的长度 """
|
||||
def size(self):
|
||||
return len(self.__stack)
|
||||
|
||||
""" 判断栈是否为空 """
|
||||
def is_empty(self):
|
||||
return self.__stack == []
|
||||
|
||||
""" 入栈 """
|
||||
def push(self, item):
|
||||
self.__stack.append(item)
|
||||
|
||||
""" 出栈 """
|
||||
def pop(self):
|
||||
assert not self.is_empty(), "栈为空"
|
||||
return self.__stack.pop()
|
||||
|
||||
""" 访问栈顶元素 """
|
||||
def peek(self):
|
||||
assert not self.is_empty(), "栈为空"
|
||||
return self.__stack[-1]
|
||||
[class]{ArrayStack}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
125
docs/chapter_tree/avl_tree.md
Normal file → Executable file
125
docs/chapter_tree/avl_tree.md
Normal file → Executable file
|
@ -179,17 +179,9 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
|
|||
=== "Python"
|
||||
|
||||
```python title="avl_tree.py"
|
||||
""" 获取结点高度 """
|
||||
def height(self, node: Optional[TreeNode]) -> int:
|
||||
# 空结点高度为 -1 ,叶结点高度为 0
|
||||
if node is not None:
|
||||
return node.height
|
||||
return -1
|
||||
[class]{AVLTree}-[func]{height}
|
||||
|
||||
""" 更新结点高度 """
|
||||
def __update_height(self, node: Optional[TreeNode]):
|
||||
# 结点高度等于最高子树高度 + 1
|
||||
node.height = max([self.height(node.left), self.height(node.right)]) + 1
|
||||
[class]{AVLTree}-[func]{__update_height}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -316,13 +308,7 @@ G. M. Adelson-Velsky 和 E. M. Landis 在其 1962 年发表的论文 "An algorit
|
|||
=== "Python"
|
||||
|
||||
```python title="avl_tree.py"
|
||||
""" 获取平衡因子 """
|
||||
def balance_factor(self, node: Optional[TreeNode]) -> int:
|
||||
# 空结点平衡因子为 0
|
||||
if node is None:
|
||||
return 0
|
||||
# 结点平衡因子 = 左子树高度 - 右子树高度
|
||||
return self.height(node.left) - self.height(node.right)
|
||||
[class]{AVLTree}-[func]{balance_factor}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -467,18 +453,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
=== "Python"
|
||||
|
||||
```python title="avl_tree.py"
|
||||
""" 右旋操作 """
|
||||
def __right_rotate(self, node: Optional[TreeNode]) -> TreeNode:
|
||||
child = node.left
|
||||
grand_child = child.right
|
||||
# 以 child 为原点,将 node 向右旋转
|
||||
child.right = node
|
||||
node.left = grand_child
|
||||
# 更新结点高度
|
||||
self.__update_height(node)
|
||||
self.__update_height(child)
|
||||
# 返回旋转后子树的根节点
|
||||
return child
|
||||
[class]{AVLTree}-[func]{__right_rotate}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -623,18 +598,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
=== "Python"
|
||||
|
||||
```python title="avl_tree.py"
|
||||
""" 左旋操作 """
|
||||
def __left_rotate(self, node: Optional[TreeNode]) -> TreeNode:
|
||||
child = node.right
|
||||
grand_child = child.left
|
||||
# 以 child 为原点,将 node 向左旋转
|
||||
child.left = node
|
||||
node.right = grand_child
|
||||
# 更新结点高度
|
||||
self.__update_height(node)
|
||||
self.__update_height(child)
|
||||
# 返回旋转后子树的根节点
|
||||
return child
|
||||
[class]{AVLTree}-[func]{__left_rotate}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -835,30 +799,7 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
=== "Python"
|
||||
|
||||
```python title="avl_tree.py"
|
||||
""" 执行旋转操作,使该子树重新恢复平衡 """
|
||||
def __rotate(self, node: Optional[TreeNode]) -> TreeNode:
|
||||
# 获取结点 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
|
||||
[class]{AVLTree}-[func]{__rotate}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1088,27 +1029,9 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
=== "Python"
|
||||
|
||||
```python title="avl_tree.py"
|
||||
""" 插入结点 """
|
||||
def insert(self, val) -> TreeNode:
|
||||
self.root = self.__insert_helper(self.root, val)
|
||||
return self.root
|
||||
[class]{AVLTree}-[func]{insert}
|
||||
|
||||
""" 递归插入结点(辅助函数)"""
|
||||
def __insert_helper(self, node: Optional[TreeNode], 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)
|
||||
[class]{AVLTree}-[func]{__insert_helper}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -1340,37 +1263,9 @@ AVL 树的独特之处在于「旋转 Rotation」的操作,其可 **在不影
|
|||
=== "Python"
|
||||
|
||||
```python title="avl_tree.py"
|
||||
""" 删除结点 """
|
||||
def remove(self, val: int):
|
||||
root = self.__remove_helper(self.root, val)
|
||||
return root
|
||||
[class]{AVLTree}-[func]{remove}
|
||||
|
||||
""" 递归删除结点(辅助函数) """
|
||||
def __remove_helper(self, node: Optional[TreeNode], val: int) -> Optional[TreeNode]:
|
||||
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 = self.__get_inorder_next(node.right)
|
||||
node.right = self.__remove_helper(node.right, temp.val)
|
||||
node.val = temp.val
|
||||
# 更新结点高度
|
||||
self.__update_height(node)
|
||||
# 2. 执行旋转操作,使该子树重新恢复平衡
|
||||
return self.__rotate(node)
|
||||
[class]{AVLTree}-[func]{__remove_helper}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
101
docs/chapter_tree/binary_search_tree.md
Normal file → Executable file
101
docs/chapter_tree/binary_search_tree.md
Normal file → Executable file
|
@ -78,21 +78,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="binary_search_tree.py"
|
||||
""" 查找结点 """
|
||||
def search(self, num: int) -> Optional[TreeNode]:
|
||||
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
|
||||
[class]{BinarySearchTree}-[func]{search}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -286,36 +272,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="binary_search_tree.py"
|
||||
""" 插入结点 """
|
||||
def insert(self, num: int) -> Optional[TreeNode]:
|
||||
root = self.root
|
||||
# 若树为空,直接提前返回
|
||||
if root is None:
|
||||
return None
|
||||
|
||||
cur = root
|
||||
pre = None
|
||||
|
||||
# 循环查找,越过叶结点后跳出
|
||||
while cur is not None:
|
||||
# 找到重复结点,直接返回
|
||||
if cur.val == num:
|
||||
return None
|
||||
pre = cur
|
||||
# 插入位置在 cur 的右子树中
|
||||
if cur.val < num:
|
||||
cur = cur.right
|
||||
# 插入位置在 cur 的左子树中
|
||||
else:
|
||||
cur = cur.left
|
||||
|
||||
# 插入结点 val
|
||||
node = TreeNode(num)
|
||||
if pre.val < num:
|
||||
pre.right = node
|
||||
else:
|
||||
pre.left = node
|
||||
return node
|
||||
[class]{BinarySearchTree}-[func]{insert}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -640,59 +597,9 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="binary_search_tree.py"
|
||||
""" 删除结点 """
|
||||
def remove(self, num: int) -> Optional[TreeNode]:
|
||||
root = self.root
|
||||
# 若树为空,直接提前返回
|
||||
if root is None:
|
||||
return None
|
||||
|
||||
cur = root
|
||||
pre = None
|
||||
|
||||
# 循环查找,越过叶结点后跳出
|
||||
while cur is not None:
|
||||
# 找到待删除结点,跳出循环
|
||||
if cur.val == num:
|
||||
break
|
||||
pre = cur
|
||||
if cur.val < num: # 待删除结点在 cur 的右子树中
|
||||
cur = cur.right
|
||||
else: # 待删除结点在 cur 的左子树中
|
||||
cur = cur.left
|
||||
|
||||
# 若无待删除结点,则直接返回
|
||||
if cur is None:
|
||||
return None
|
||||
|
||||
# 子结点数量 = 0 or 1
|
||||
if cur.left is None or cur.right is None:
|
||||
# 当子结点数量 = 0 / 1 时, child = null / 该子结点
|
||||
child = cur.left or cur.right
|
||||
# 删除结点 cur
|
||||
if pre.left == cur:
|
||||
pre.left = child
|
||||
else:
|
||||
pre.right = child
|
||||
# 子结点数量 = 2
|
||||
else:
|
||||
# 获取中序遍历中 cur 的下一个结点
|
||||
nex = self.get_inorder_next(cur.right)
|
||||
tmp = nex.val
|
||||
# 递归删除结点 nex
|
||||
self.remove(nex.val)
|
||||
# 将 nex 的值复制给 cur
|
||||
cur.val = tmp
|
||||
return cur
|
||||
[class]{BinarySearchTree}-[func]{remove}
|
||||
|
||||
""" 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) """
|
||||
def get_inorder_next(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
|
||||
if root is None:
|
||||
return root
|
||||
# 循环访问左子结点,直到叶结点时为最小结点,跳出
|
||||
while root.left is not None:
|
||||
root = root.left
|
||||
return root
|
||||
[class]{BinarySearchTree}-[func]{get_inorder_next}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
43
docs/chapter_tree/binary_tree_traversal.md
Normal file → Executable file
43
docs/chapter_tree/binary_tree_traversal.md
Normal file → Executable file
|
@ -65,21 +65,7 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="binary_tree_bfs.py"
|
||||
""" 层序遍历 """
|
||||
def hier_order(root: Optional[TreeNode]):
|
||||
# 初始化队列,加入根结点
|
||||
queue = collections.deque()
|
||||
queue.append(root)
|
||||
# 初始化一个列表,用于保存遍历序列
|
||||
res = []
|
||||
while queue:
|
||||
node = 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
|
||||
[class]{}-[func]{hier_order}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
@ -299,32 +285,11 @@ comments: true
|
|||
=== "Python"
|
||||
|
||||
```python title="binary_tree_dfs.py"
|
||||
""" 前序遍历 """
|
||||
def pre_order(root: Optional[TreeNode]):
|
||||
if root is None:
|
||||
return
|
||||
# 访问优先级:根结点 -> 左子树 -> 右子树
|
||||
res.append(root.val)
|
||||
pre_order(root=root.left)
|
||||
pre_order(root=root.right)
|
||||
[class]{}-[func]{pre_order}
|
||||
|
||||
""" 中序遍历 """
|
||||
def in_order(root: Optional[TreeNode]):
|
||||
if root is None:
|
||||
return
|
||||
# 访问优先级:左子树 -> 根结点 -> 右子树
|
||||
in_order(root=root.left)
|
||||
res.append(root.val)
|
||||
in_order(root=root.right)
|
||||
[class]{}-[func]{in_order}
|
||||
|
||||
""" 后序遍历 """
|
||||
def post_order(root: Optional[TreeNode]):
|
||||
if root is None:
|
||||
return
|
||||
# 访问优先级:左子树 -> 右子树 -> 根结点
|
||||
post_order(root=root.left)
|
||||
post_order(root=root.right)
|
||||
res.append(root.val)
|
||||
[class]{}-[func]{post_order}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
|
1
docs/utils/.gitignore
vendored
Normal file
1
docs/utils/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
__pycache__
|
87
docs/utils/build_markdown.py
Executable file
87
docs/utils/build_markdown.py
Executable file
|
@ -0,0 +1,87 @@
|
|||
"""
|
||||
File: build_markdown_docs.py
|
||||
Created Time: 2023-02-06
|
||||
Author: Krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys, os.path as osp
|
||||
sys.path.append(osp.dirname(osp.dirname(osp.dirname(osp.abspath(__file__)))))
|
||||
|
||||
import re
|
||||
import glob
|
||||
import shutil
|
||||
from docs.utils.extract_code_python import ExtractCodeBlocksPython
|
||||
|
||||
def build_markdown(md_path):
|
||||
with open(md_path, "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
code_blocks_dict = {}
|
||||
file_pattern = re.compile(r'\s*```(\w+)\s+title="(.+)"')
|
||||
src_pattern = re.compile(r'\s*\[class\]\{(.*?)\}-\[func\]\{(.*?)\}')
|
||||
for i in range(len(lines)):
|
||||
# Find the line target to the source codes
|
||||
src_match = src_pattern.match(lines[i])
|
||||
if src_match is None:
|
||||
continue
|
||||
for j in range(i - 1, -1 ,-1):
|
||||
file_match = file_pattern.match(lines[j])
|
||||
if file_match is not None:
|
||||
break
|
||||
|
||||
# Get code blocks
|
||||
lang = file_match[1]
|
||||
file_name = file_match[2]
|
||||
if file_name not in code_blocks_dict:
|
||||
code_blocks_dict[file_name] = ExtractCodeBlocksPython(
|
||||
file_path=osp.dirname(md_path).replace("docs/", f"codes/{lang}/") + f"/{file_name}")
|
||||
|
||||
header_line = i
|
||||
class_label = src_match[1]
|
||||
func_label = src_match[2]
|
||||
code_blocks = code_blocks_dict[file_name]
|
||||
src_info = {
|
||||
"line_number": i,
|
||||
"class_label": src_match[1],
|
||||
"func_label": src_match[2],
|
||||
"code_blocks": code_blocks_dict[file_name]
|
||||
}
|
||||
|
||||
# Add the class to the doc
|
||||
if not func_label and class_label:
|
||||
if class_label in code_blocks.classes:
|
||||
lines.pop(header_line)
|
||||
class_block = code_blocks.classes[class_label]["block"]
|
||||
for code_line in class_block[::-1]:
|
||||
ind = " " * 4 if code_line != "\n" else ""
|
||||
lines.insert(header_line, ind + code_line)
|
||||
# Add the function to the doc
|
||||
elif func_label and not class_label:
|
||||
if func_label in code_blocks.functions:
|
||||
lines.pop(header_line)
|
||||
func_block = code_blocks.functions[func_label]
|
||||
for code_line in func_block["block"][::-1]:
|
||||
ind = " " * 4 if code_line != "\n" else ""
|
||||
lines.insert(header_line, ind + code_line)
|
||||
# Add the class method to the doc
|
||||
elif func_label and class_label:
|
||||
if class_label in code_blocks.classes:
|
||||
class_dict = code_blocks.classes[class_label]
|
||||
if func_label in class_dict["functions"]:
|
||||
lines.pop(header_line)
|
||||
func_block = class_dict["functions"][func_label]
|
||||
for code_line in func_block["block"][::-1]:
|
||||
lines.insert(header_line, code_line)
|
||||
|
||||
with open(md_path.replace("docs/", "build/"), "w") as f:
|
||||
f.writelines(lines)
|
||||
print(f"Built {md_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Copy files to the build dir
|
||||
shutil.copytree("docs", "build", dirs_exist_ok=True)
|
||||
shutil.rmtree("build/utils")
|
||||
# Build docs
|
||||
for md_path in glob.glob("docs/chapter_*/*.md"):
|
||||
build_markdown(md_path)
|
8
docs/utils/deploy.sh
Normal file
8
docs/utils/deploy.sh
Normal file
|
@ -0,0 +1,8 @@
|
|||
# This script is borrowed from https://gist.github.com/cobyism/4730490
|
||||
|
||||
git add build && git commit -m "build"
|
||||
git subtree push --prefix build origin built-docs
|
||||
|
||||
mkdocs build --clean
|
||||
git add site && git commit -m "deploy"
|
||||
git subtree push --prefix site origin gh-pages
|
117
docs/utils/extract_code_python.py
Executable file
117
docs/utils/extract_code_python.py
Executable file
|
@ -0,0 +1,117 @@
|
|||
"""
|
||||
File: extract_code_python.py
|
||||
Created Time: 2023-02-06
|
||||
Author: Krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import re
|
||||
import os
|
||||
import os.path as osp
|
||||
import glob
|
||||
|
||||
class ExtractCodeBlocksPython:
|
||||
def __init__(self, file_path) -> None:
|
||||
self.file_path = file_path
|
||||
with open(file_path) as f:
|
||||
self.lines = f.readlines()
|
||||
self.content = "".join(self.lines)
|
||||
|
||||
# Regular expression pattern to match function names and class names
|
||||
self.func_pattern = re.compile(r'(\s*)def\s+(\w+)\s*\(')
|
||||
self.class_pattern = re.compile(r'class\s+(\w+)')
|
||||
|
||||
# Detect and extract all the classes and fucntions
|
||||
self.classes = self.extract_class_blocks()
|
||||
self.functions = self.extract_function_blocks()
|
||||
|
||||
def search_block(self, header_line, indentation):
|
||||
"""
|
||||
Search class/function block given the header_line and indentation
|
||||
"""
|
||||
start_line, end_line = 0, len(self.lines)
|
||||
# Search the code
|
||||
for i in range(header_line + 1, len(self.lines)):
|
||||
if re.search("^\s*\n|^\s{ind}\s+.+\n".replace("ind", str(indentation)),
|
||||
self.lines[i]) is None:
|
||||
end_line = i
|
||||
break
|
||||
# Search the header comment
|
||||
for i in range(header_line - 1, -1, -1):
|
||||
if re.search('^\s{ind}""".+'.replace("ind", str(indentation)),
|
||||
self.lines[i]) is not None:
|
||||
start_line = i
|
||||
break
|
||||
func_block = self.lines[start_line:end_line]
|
||||
# Remove empty lines at bottom
|
||||
for i in range(len(func_block) - 1, -1, -1):
|
||||
if re.search("^\s*\n", func_block[i]) is None:
|
||||
break
|
||||
end_line -= 1
|
||||
|
||||
return start_line, end_line, self.lines[start_line:end_line]
|
||||
|
||||
|
||||
def extract_function_blocks(self, indentation=0, start_line=-1, end_line=-1):
|
||||
"""
|
||||
Extract all the functions with given indentation
|
||||
"""
|
||||
functions = {}
|
||||
|
||||
if start_line == -1:
|
||||
start_line = 0
|
||||
if end_line == -1:
|
||||
end_line = len(self.lines) - 1
|
||||
|
||||
for line_num in range(start_line, end_line + 1):
|
||||
# Search the function header
|
||||
func_match = self.func_pattern.match(self.lines[line_num])
|
||||
if func_match is None: continue
|
||||
# The function should match the input indentation
|
||||
if len(func_match.group(1)) != indentation: continue
|
||||
header_line = line_num
|
||||
|
||||
# Search the block from the header line
|
||||
start_line, end_line, func_block = self.search_block(header_line, indentation)
|
||||
# Construct the functions dict
|
||||
func_label = func_match.group(2)
|
||||
functions[func_label] = {
|
||||
"indentation": indentation,
|
||||
"line_number": {
|
||||
"start": start_line,
|
||||
"end": end_line,
|
||||
"header": header_line,
|
||||
},
|
||||
"block": func_block,
|
||||
}
|
||||
|
||||
return functions
|
||||
|
||||
def extract_class_blocks(self):
|
||||
"""
|
||||
Extract all the classes with given indentation
|
||||
"""
|
||||
classes = {}
|
||||
|
||||
for line_num, line in enumerate(self.lines):
|
||||
# Search the class header
|
||||
class_match = self.class_pattern.match(line)
|
||||
if class_match is None: continue
|
||||
header_line = line_num
|
||||
|
||||
# Search the block from the header line
|
||||
start_line, end_line, class_block = self.search_block(header_line, 0)
|
||||
# Construct the classes dict
|
||||
class_label = class_match.group(1)
|
||||
classes[class_label] = {
|
||||
"indentation": 0,
|
||||
"line_number": {
|
||||
"start": start_line,
|
||||
"end": end_line,
|
||||
"header": header_line,
|
||||
},
|
||||
"block": class_block,
|
||||
"functions": self.extract_function_blocks(
|
||||
indentation=4, start_line=start_line, end_line=end_line)
|
||||
}
|
||||
|
||||
return classes
|
|
@ -3,11 +3,11 @@ site_name: Hello 算法
|
|||
site_url: https://www.hello-algo.com/
|
||||
site_author: Krahets
|
||||
site_description: 一本动画图解、能运行、可提问的数据结构与算法入门书
|
||||
docs_dir: docs
|
||||
docs_dir: build
|
||||
# Repository
|
||||
repo_name: krahets/hello-algo
|
||||
repo_url: https://github.com/krahets/hello-algo
|
||||
edit_uri: https://github.com/krahets/hello-algo/tree/master/docs/
|
||||
edit_uri: https://github.com/krahets/hello-algo/tree/main/docs/
|
||||
|
||||
# Copyright
|
||||
copyright: Copyright © 2022 Krahets
|
||||
|
@ -15,7 +15,7 @@ copyright: Copyright © 2022 Krahets
|
|||
# Configuration
|
||||
theme:
|
||||
name: material
|
||||
custom_dir: docs/overrides
|
||||
custom_dir: build/overrides
|
||||
language: zh
|
||||
features:
|
||||
# - announce.dismiss
|
||||
|
|
Loading…
Reference in a new issue