mirror of
https://github.com/krahets/hello-algo.git
synced 2026-11-10 07:49:44 +08:00
Retranslate the complete Japanese online edition from Chinese (#2008)
* Retranslate the complete Japanese online edition from Chinese * Fit Japanese homepage labels and localize heading permalinks * Point Japanese contribution instructions to the Japanese preview * Replace locale conditionals with multilingual UI tables
This commit is contained in:
@@ -8,21 +8,21 @@ import random
|
||||
|
||||
|
||||
def random_access(nums: list[int]) -> int:
|
||||
"""要素へランダムアクセス"""
|
||||
# 区間 [0, len(nums)-1] からランダムに数字を 1 つ選ぶ
|
||||
"""要素にランダムアクセスする"""
|
||||
# 区間 [0, len(nums)-1] から数値をランダムに 1 つ選ぶ
|
||||
random_index = random.randint(0, len(nums) - 1)
|
||||
# ランダムな要素を取得して返す
|
||||
# ランダムに選んだ要素を取得して返す
|
||||
random_num = nums[random_index]
|
||||
return random_num
|
||||
|
||||
|
||||
# Python の list は動的配列であり、直接拡張できます
|
||||
# 学習しやすいよう、本関数では list を長さ不変の配列として扱います
|
||||
# Python の list は動的配列なので、そのまま拡張できることに注意
|
||||
# 学習しやすいように、この関数では 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]
|
||||
# 拡張後の新しい配列を返す
|
||||
@@ -30,28 +30,28 @@ def extend(nums: list[int], enlarge: int) -> list[int]:
|
||||
|
||||
|
||||
def insert(nums: list[int], num: int, index: int):
|
||||
"""配列の index 番目に要素 num を挿入"""
|
||||
# インデックス index 以降の全要素を 1 つ後ろへ移動する
|
||||
"""配列のインデックス index に要素 num を挿入する"""
|
||||
# インデックス index 以降の全要素を 1 つ後ろにずらす
|
||||
for i in range(len(nums) - 1, index, -1):
|
||||
nums[i] = nums[i - 1]
|
||||
# index の要素に num を代入する
|
||||
# インデックス index の要素に num を代入する
|
||||
nums[index] = num
|
||||
|
||||
|
||||
def remove(nums: list[int], index: int):
|
||||
"""index の要素を削除する"""
|
||||
# インデックス index より後ろの全要素を 1 つ前へ移動する
|
||||
"""インデックス index の要素を削除する"""
|
||||
# インデックス index より後の全要素を 1 つ前にずらす
|
||||
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
|
||||
# データのインデックスと要素を同時に走査する
|
||||
@@ -61,16 +61,16 @@ def traverse(nums: list[int]):
|
||||
|
||||
|
||||
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]
|
||||
@@ -78,23 +78,23 @@ if __name__ == "__main__":
|
||||
|
||||
# ランダムアクセス
|
||||
random_num: int = random_access(nums)
|
||||
print("nums からランダムな要素を取得", random_num)
|
||||
print("nums からランダムに取得した要素", random_num)
|
||||
|
||||
# 長さを拡張
|
||||
# 長さの拡張
|
||||
nums: list[int] = extend(nums, 3)
|
||||
print("配列の長さを 8 に拡張し、nums =", nums)
|
||||
print("配列の長さを 8 に拡張した結果 nums =", nums)
|
||||
|
||||
# 要素を挿入する
|
||||
insert(nums, 6, 3)
|
||||
print("インデックス 3 に数値 6 を挿入し、nums =", nums)
|
||||
print("インデックス 3 に数値 6 を挿入した結果 nums =", nums)
|
||||
|
||||
# 要素を削除
|
||||
# 要素を削除する
|
||||
remove(nums, 2)
|
||||
print("インデックス 2 の要素を削除し、nums =", nums)
|
||||
print("インデックス 2 の要素を削除した結果 nums =", nums)
|
||||
|
||||
# 配列を走査
|
||||
# 配列を走査する
|
||||
traverse(nums)
|
||||
|
||||
# 要素を探索する
|
||||
index: int = find(nums, 3)
|
||||
print("nums で要素 3 を検索し、インデックス =", index)
|
||||
print("nums から要素 3 を探索した結果のインデックス =", index)
|
||||
|
||||
@@ -12,14 +12,14 @@ from modules import ListNode, print_linked_list
|
||||
|
||||
|
||||
def insert(n0: ListNode, P: ListNode):
|
||||
"""連結リストでノード n0 の後ろにノード P を挿入する"""
|
||||
"""連結リストのノード n0 の直後にノード P を挿入する"""
|
||||
n1 = n0.next
|
||||
P.next = n1
|
||||
n0.next = P
|
||||
|
||||
|
||||
def remove(n0: ListNode):
|
||||
"""連結リストでノード n0 の直後のノードを削除する"""
|
||||
"""連結リストのノード n0 の直後のノードを削除する"""
|
||||
if not n0.next:
|
||||
return
|
||||
# n0 -> P -> n1
|
||||
@@ -29,7 +29,7 @@ def remove(n0: ListNode):
|
||||
|
||||
|
||||
def access(head: ListNode, index: int) -> ListNode | None:
|
||||
"""連結リスト内で index 番目のノードにアクセス"""
|
||||
"""連結リストのインデックス index にあるノードにアクセスする"""
|
||||
for _ in range(index):
|
||||
if not head:
|
||||
return None
|
||||
@@ -38,7 +38,7 @@ def access(head: ListNode, index: int) -> ListNode | None:
|
||||
|
||||
|
||||
def find(head: ListNode, target: int) -> int:
|
||||
"""連結リストで値が target の最初のノードを探す"""
|
||||
"""連結リストから値が target である最初のノードを探索する"""
|
||||
index = 0
|
||||
while head:
|
||||
if head.val == target:
|
||||
@@ -48,7 +48,7 @@ def find(head: ListNode, target: int) -> int:
|
||||
return -1
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# 連結リストを初期化する
|
||||
# 各ノードを初期化する
|
||||
@@ -57,29 +57,29 @@ if __name__ == "__main__":
|
||||
n2 = ListNode(2)
|
||||
n3 = ListNode(5)
|
||||
n4 = ListNode(4)
|
||||
# ノード間の参照を構築する
|
||||
# ノード間の参照を設定する
|
||||
n0.next = n1
|
||||
n1.next = n2
|
||||
n2.next = n3
|
||||
n3.next = n4
|
||||
print("初期化した連結リストは")
|
||||
print("初期化した連結リスト")
|
||||
print_linked_list(n0)
|
||||
|
||||
# ノードを挿入
|
||||
# ノードを挿入する
|
||||
p = ListNode(0)
|
||||
insert(n0, p)
|
||||
print("ノード挿入後の連結リストは")
|
||||
print("ノードを挿入した後の連結リスト")
|
||||
print_linked_list(n0)
|
||||
|
||||
# ノードを削除
|
||||
# ノードを削除する
|
||||
remove(n0)
|
||||
print("ノード削除後の連結リストは")
|
||||
print("ノードを削除した後の連結リスト")
|
||||
print_linked_list(n0)
|
||||
|
||||
# ノードにアクセス
|
||||
# ノードにアクセスする
|
||||
node: ListNode = access(n0, 3)
|
||||
print("連結リストのインデックス 3 のノードの値 = {}".format(node.val))
|
||||
print("連結リストのインデックス 3 にあるノードの値 = {}".format(node.val))
|
||||
|
||||
# ノードを探索
|
||||
# ノードを探索する
|
||||
index: int = find(n0, 2)
|
||||
print("連結リスト内で値が 2 のノードのインデックス = {}".format(index))
|
||||
print("連結リスト内で値が 2 であるノードのインデックス = {}".format(index))
|
||||
|
||||
@@ -4,53 +4,53 @@ 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)
|
||||
print("\nインデックス 1 の要素にアクセスした結果 x =", x)
|
||||
|
||||
# 要素を更新
|
||||
# 要素を更新する
|
||||
nums[1] = 0
|
||||
print("\nインデックス 1 の要素を 0 に更新し、nums =", nums)
|
||||
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)
|
||||
print("\n要素を追加した後 nums =", nums)
|
||||
|
||||
# 中間に要素を挿入
|
||||
# 途中に要素を挿入する
|
||||
nums.insert(3, 6)
|
||||
print("\nインデックス 3 に数値 6 を挿入すると、nums =", nums)
|
||||
print("\nインデックス 3 に数値 6 を挿入した結果 nums =", nums)
|
||||
|
||||
# 要素を削除
|
||||
# 要素を削除する
|
||||
nums.pop(3)
|
||||
print("\nインデックス 3 の要素を削除すると、nums =", nums)
|
||||
print("\nインデックス 3 の要素を削除した結果 nums =", nums)
|
||||
|
||||
# インデックスでリストを走査
|
||||
# インデックスを使ってリストを走査する
|
||||
count = 0
|
||||
for i in range(len(nums)):
|
||||
count += nums[i]
|
||||
# リスト要素を直接走査
|
||||
# リストの要素を直接走査する
|
||||
for num in nums:
|
||||
count += num
|
||||
|
||||
# 2 つのリストを連結する
|
||||
nums1 = [6, 8, 7, 10, 9]
|
||||
nums += nums1
|
||||
print("\nリスト nums1 を nums の後ろに連結すると、nums =", nums)
|
||||
print("\nリスト nums の後ろに nums1 を連結した結果 nums =", nums)
|
||||
|
||||
# リストをソート
|
||||
# リストをソートする
|
||||
nums.sort()
|
||||
print("\nリストをソートすると nums =", nums)
|
||||
print("\nリストをソートした後 nums =", nums)
|
||||
|
||||
@@ -10,84 +10,84 @@ class MyList:
|
||||
|
||||
def __init__(self):
|
||||
"""コンストラクタ"""
|
||||
self._capacity: int = 10 # リスト容量
|
||||
self._arr: list[int] = [0] * self._capacity # 配列(リスト要素を格納)
|
||||
self._capacity: int = 10 # リストの容量
|
||||
self._arr: list[int] = [0] * self._capacity # 配列(リストの要素を格納する)
|
||||
self._size: int = 0 # リストの長さ(現在の要素数)
|
||||
self._extend_ratio: int = 2 # リスト拡張時の増加倍率
|
||||
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 以降の要素をすべて 1 つ後ろへずらす
|
||||
# インデックス index 以降の全要素を後ろに 1 つずらす
|
||||
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 より後の要素をすべて 1 つ前に移動する
|
||||
# インデックス index より後の全要素を前に 1 つずらす
|
||||
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` 倍の長さを持つ新しい配列を作成し、元の配列を新しい配列にコピーする
|
||||
"""リストの容量を拡張する"""
|
||||
# 元の配列の _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)
|
||||
@@ -95,24 +95,24 @@ if __name__ == "__main__":
|
||||
nums.add(4)
|
||||
print(f"リスト nums = {nums.to_array()} 、容量 = {nums.capacity()} 、長さ = {nums.size()}")
|
||||
|
||||
# 中間に要素を挿入
|
||||
# 途中に要素を挿入する
|
||||
nums.insert(6, index=3)
|
||||
print("インデックス 3 に数値 6 を挿入すると、nums =", nums.to_array())
|
||||
print("インデックス 3 に数値 6 を挿入した結果 nums =", nums.to_array())
|
||||
|
||||
# 要素を削除
|
||||
# 要素を削除する
|
||||
nums.remove(3)
|
||||
print("インデックス 3 の要素を削除すると、nums =", nums.to_array())
|
||||
print("インデックス 3 の要素を削除した結果 nums =", nums.to_array())
|
||||
|
||||
# 要素にアクセス
|
||||
# 要素にアクセスする
|
||||
num = nums.get(1)
|
||||
print("インデックス 1 の要素にアクセスすると、num =", num)
|
||||
print("インデックス 1 の要素にアクセスした結果 num =", num)
|
||||
|
||||
# 要素を更新
|
||||
# 要素を更新する
|
||||
nums.set(0, 1)
|
||||
print("インデックス 1 の要素を 0 に更新すると、nums =", nums.to_array())
|
||||
print("インデックス 1 の要素を 0 に更新した結果 nums =", nums.to_array())
|
||||
|
||||
# 拡張機構をテストする
|
||||
# 容量の拡張をテストする
|
||||
for i in range(10):
|
||||
# i = 5 のとき、リスト長が容量を超えるため、この時点で拡張機構が発動する
|
||||
# i = 5 のときにリストの長さが容量を超えるため、容量が拡張される
|
||||
nums.add(i)
|
||||
print(f"拡張後のリスト {nums.to_array()} 、容量 = {nums.capacity()} 、長さ = {nums.size()}")
|
||||
print(f"容量を拡張した後のリスト {nums.to_array()} 、容量 = {nums.capacity()} 、長さ = {nums.size()}")
|
||||
|
||||
@@ -14,48 +14,48 @@ def backtrack(
|
||||
diags1: list[bool],
|
||||
diags2: list[bool],
|
||||
):
|
||||
"""バックトラッキング:N クイーン"""
|
||||
# すべての行への配置が完了したら、解を記録する
|
||||
"""バックトラッキング: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*n の盤面を初期化する。'Q' はクイーン、'#' は空きマスを表す
|
||||
state = [["#" for _ in range(n)] for _ in range(n)]
|
||||
cols = [False] * n # 列にクイーンがあるか記録
|
||||
diags1 = [False] * (2 * n - 1) # 主対角線にクイーンがあるかを記録
|
||||
diags2 = [False] * (2 * n - 1) # 副対角線にクイーンがあるかを記録
|
||||
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)} 通りです")
|
||||
print(f"入力した盤面の縦横の長さは {n}")
|
||||
print(f"クイーンの配置方法は全部で {len(res)} 通り")
|
||||
for state in res:
|
||||
print("--------------------")
|
||||
for row in state:
|
||||
|
||||
@@ -8,33 +8,33 @@ Author: krahets (krahets@163.com)
|
||||
def backtrack(
|
||||
state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]
|
||||
):
|
||||
"""バックトラッキング:順列 I"""
|
||||
# 状態の長さが要素数に等しければ、解を記録
|
||||
"""バックトラッキング:すべての順列 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"""
|
||||
"""すべての順列 I"""
|
||||
res = []
|
||||
backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
nums = [1, 2, 3]
|
||||
|
||||
|
||||
@@ -8,35 +8,35 @@ Author: krahets (krahets@163.com)
|
||||
def backtrack(
|
||||
state: list[int], choices: list[int], selected: list[bool], res: list[list[int]]
|
||||
):
|
||||
"""バックトラッキング:順列 II"""
|
||||
# 状態の長さが要素数に等しければ、解を記録
|
||||
"""バックトラッキング:すべての順列 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) # 選択済みの要素値を記録
|
||||
# 試行:選択を行い、状態を更新する
|
||||
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"""
|
||||
"""すべての順列 II"""
|
||||
res = []
|
||||
backtrack(state=[], choices=nums, selected=[False] * len(nums), res=res)
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
nums = [1, 2, 2]
|
||||
|
||||
|
||||
@@ -12,25 +12,25 @@ from modules import TreeNode, print_tree, list_to_tree
|
||||
|
||||
|
||||
def pre_order(root: TreeNode):
|
||||
"""前順走査:例題 1"""
|
||||
"""行きがけ順走査:例題 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("\n二分木を初期化する")
|
||||
print_tree(root)
|
||||
|
||||
# 先行順走査
|
||||
# 行きがけ順走査
|
||||
res = list[TreeNode]()
|
||||
pre_order(root)
|
||||
|
||||
print("\n値が 7 のノードをすべて出力")
|
||||
print("\n値が 7 であるすべてのノードを出力する")
|
||||
print([node.val for node in res])
|
||||
|
||||
@@ -12,31 +12,31 @@ from modules import TreeNode, print_tree, list_to_tree
|
||||
|
||||
|
||||
def pre_order(root: TreeNode):
|
||||
"""前順走査:例題 2"""
|
||||
"""行きがけ順走査:例題 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("\n二分木を初期化する")
|
||||
print_tree(root)
|
||||
|
||||
# 先行順走査
|
||||
# 行きがけ順走査
|
||||
path = list[TreeNode]()
|
||||
res = list[list[TreeNode]]()
|
||||
pre_order(root)
|
||||
|
||||
print("\n根ノードからノード 7 までの経路をすべて出力")
|
||||
print("\n根ノードからノード 7 までのすべての経路を出力する")
|
||||
for path in res:
|
||||
print([node.val for node in path])
|
||||
|
||||
@@ -12,32 +12,32 @@ from modules import TreeNode, print_tree, list_to_tree
|
||||
|
||||
|
||||
def pre_order(root: TreeNode):
|
||||
"""前順走査:例題 3"""
|
||||
"""行きがけ順走査:例題 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("\n二分木を初期化する")
|
||||
print_tree(root)
|
||||
|
||||
# 先行順走査
|
||||
# 行きがけ順走査
|
||||
path = list[TreeNode]()
|
||||
res = list[list[TreeNode]]()
|
||||
pre_order(root)
|
||||
|
||||
print("\n根ノードからノード 7 までの経路をすべて出力し、経路には値が 3 のノードを含めない")
|
||||
print("\n値が 3 のノードを含まない、根ノードからノード 7 までのすべての経路を出力する")
|
||||
for path in res:
|
||||
print([node.val for node in path])
|
||||
|
||||
@@ -12,27 +12,27 @@ 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()
|
||||
|
||||
|
||||
@@ -40,32 +40,32 @@ 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("\n二分木を初期化する")
|
||||
print_tree(root)
|
||||
|
||||
# バックトラッキング法
|
||||
# バックトラッキング
|
||||
res = []
|
||||
backtrack(state=[], choices=[root], res=res)
|
||||
|
||||
print("\n根ノードからノード 7 までの経路をすべて出力し、経路には値が 3 のノードを含まないことを条件とする")
|
||||
print("\n値が 3 のノードを含まない、根ノードからノード 7 までのすべての経路を出力する")
|
||||
for path in res:
|
||||
print([node.val for node in path])
|
||||
|
||||
@@ -8,41 +8,41 @@ Author: krahets (krahets@163.com)
|
||||
def backtrack(
|
||||
state: list[int], target: int, choices: list[int], start: int, res: list[list[int]]
|
||||
):
|
||||
"""バックトラッキング:部分和 I"""
|
||||
# 部分集合の和が target に等しければ、解を記録
|
||||
"""バックトラッキング:部分集合和 I"""
|
||||
# 部分集合の和が target と等しくなったら、解を記録する
|
||||
if target == 0:
|
||||
res.append(list(state))
|
||||
return
|
||||
# すべての選択肢を走査
|
||||
# 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
|
||||
# すべての選択肢を走査する
|
||||
# 枝刈り 2:start から走査を始め、重複した部分集合の生成を避ける
|
||||
for i in range(start, len(choices)):
|
||||
# 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
|
||||
# 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
|
||||
# 枝刈り 1:部分集合の和が target を超えたら、直ちにループを終了する
|
||||
# 配列はソート済みで後ろの要素ほど大きいため、部分集合の和が必ず target を超えるからである
|
||||
if target - choices[i] < 0:
|
||||
break
|
||||
# 試す:選択を行い、target と start を更新
|
||||
# 試行:選択を行い、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 を解く"""
|
||||
"""部分集合和 I を解く"""
|
||||
state = [] # 状態(部分集合)
|
||||
nums.sort() # nums をソート
|
||||
start = 0 # 開始点を走査
|
||||
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}")
|
||||
print(f"和が {target} になるすべての部分集合 res = {res}")
|
||||
|
||||
@@ -12,39 +12,39 @@ def backtrack(
|
||||
choices: list[int],
|
||||
res: list[list[int]],
|
||||
):
|
||||
"""バックトラッキング:部分和 I"""
|
||||
# 部分集合の和が target に等しければ、解を記録
|
||||
"""バックトラッキング:部分集合和 I"""
|
||||
# 部分集合の和が target と等しくなったら、解を記録する
|
||||
if total == target:
|
||||
res.append(list(state))
|
||||
return
|
||||
# すべての選択肢を走査
|
||||
# すべての選択肢を走査する
|
||||
for i in range(len(choices)):
|
||||
# 枝刈り:部分和が target を超える場合はその選択をスキップする
|
||||
# 枝刈り:部分集合の和が target を超える場合、この選択をスキップする
|
||||
if total + choices[i] > target:
|
||||
continue
|
||||
# 試行:選択を行い、要素と total を更新する
|
||||
# 試行:選択を行い、要素の和 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 を解く(重複部分集合を含む)"""
|
||||
"""部分集合和 I を解く(重複した部分集合を含む)"""
|
||||
state = [] # 状態(部分集合)
|
||||
total = 0 # 部分和
|
||||
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"注意: この方法の出力結果には重複する集合が含まれます")
|
||||
print(f"和が {target} になるすべての部分集合 res = {res}")
|
||||
print(f"この方法の出力結果には重複した集合が含まれることに注意")
|
||||
|
||||
@@ -8,45 +8,45 @@ Author: krahets (krahets@163.com)
|
||||
def backtrack(
|
||||
state: list[int], target: int, choices: list[int], start: int, res: list[list[int]]
|
||||
):
|
||||
"""バックトラッキング:部分和 II"""
|
||||
# 部分集合の和が target に等しければ、解を記録
|
||||
"""バックトラッキング:部分集合和 II"""
|
||||
# 部分集合の和が target と等しくなったら、解を記録する
|
||||
if target == 0:
|
||||
res.append(list(state))
|
||||
return
|
||||
# すべての選択肢を走査
|
||||
# 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
|
||||
# 枝刈り 3: start から走査し、同じ要素の重複選択を避ける
|
||||
# すべての選択肢を走査する
|
||||
# 枝刈り 2:start から走査を始め、重複した部分集合の生成を避ける
|
||||
# 枝刈り 3:start から走査を始め、同じ要素の再選択を避ける
|
||||
for i in range(start, len(choices)):
|
||||
# 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
|
||||
# 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
|
||||
# 枝刈り 1:部分集合の和が target を超えたら、直ちにループを終了する
|
||||
# 配列はソート済みで後ろの要素ほど大きいため、部分集合の和が必ず target を超えるからである
|
||||
if target - choices[i] < 0:
|
||||
break
|
||||
# 枝刈り4:この要素が左隣の要素と等しければ、その探索分岐は重複しているためスキップする
|
||||
# 枝刈り 4:この要素が左隣の要素と等しい場合、探索の分岐が重複するため、直ちにスキップする
|
||||
if i > start and choices[i] == choices[i - 1]:
|
||||
continue
|
||||
# 試す:選択を行い、target と start を更新
|
||||
# 試行:選択を行い、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 を解く"""
|
||||
"""部分集合和 II を解く"""
|
||||
state = [] # 状態(部分集合)
|
||||
nums.sort() # nums をソート
|
||||
start = 0 # 開始点を走査
|
||||
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}")
|
||||
print(f"和が {target} になるすべての部分集合 res = {res}")
|
||||
|
||||
@@ -21,7 +21,7 @@ def sum_recur(n: int) -> int:
|
||||
|
||||
|
||||
def linear_loop(n: int) -> int:
|
||||
"""線形時間のループ"""
|
||||
"""線形オーダーのループ"""
|
||||
res = 0
|
||||
for i in range(n):
|
||||
res += i
|
||||
@@ -29,7 +29,7 @@ def linear_loop(n: int) -> int:
|
||||
|
||||
|
||||
def quadratic_loop(n: int) -> int:
|
||||
"""二次時間のループ"""
|
||||
"""二次オーダーのループ"""
|
||||
res = 0
|
||||
for i in range(n):
|
||||
for j in range(i, n):
|
||||
@@ -38,7 +38,7 @@ def quadratic_loop(n: int) -> int:
|
||||
|
||||
|
||||
def logarithmic_loop(n: int) -> int:
|
||||
"""対数時間のループ"""
|
||||
"""対数オーダーのループ"""
|
||||
while n > 1:
|
||||
n //= 2
|
||||
return n
|
||||
|
||||
@@ -8,7 +8,7 @@ Author: krahets (krahets@163.com)
|
||||
def for_loop(n: int) -> int:
|
||||
"""for ループ"""
|
||||
res = 0
|
||||
# 1, 2, ..., n-1, n を順に加算する
|
||||
# ループで 1, 2, ..., n-1, n の和を求める
|
||||
for i in range(1, n + 1):
|
||||
res += i
|
||||
return res
|
||||
@@ -18,7 +18,7 @@ def while_loop(n: int) -> int:
|
||||
"""while ループ"""
|
||||
res = 0
|
||||
i = 1 # 条件変数を初期化する
|
||||
# 1, 2, ..., n-1, n を順に加算する
|
||||
# ループで 1, 2, ..., n-1, n の和を求める
|
||||
while i <= n:
|
||||
res += i
|
||||
i += 1 # 条件変数を更新する
|
||||
@@ -26,10 +26,10 @@ def while_loop(n: int) -> int:
|
||||
|
||||
|
||||
def while_loop_ii(n: int) -> int:
|
||||
"""while ループ(2回更新)"""
|
||||
"""while ループ(2 回の更新)"""
|
||||
res = 0
|
||||
i = 1 # 条件変数を初期化する
|
||||
# 1, 4, 10, ... を順に加算する
|
||||
# ループで 1, 4, 10, ... の和を求める
|
||||
while i <= n:
|
||||
res += i
|
||||
# 条件変数を更新する
|
||||
@@ -41,25 +41,25 @@ def while_loop_ii(n: int) -> int:
|
||||
def nested_for_loop(n: int) -> str:
|
||||
"""二重 for ループ"""
|
||||
res = ""
|
||||
# i = 1, 2, ..., n-1, n とループする
|
||||
# i = 1, 2, ..., n-1, n のループ
|
||||
for i in range(1, n + 1):
|
||||
# j = 1, 2, ..., n-1, n とループする
|
||||
# j = 1, 2, ..., n-1, n のループ
|
||||
for j in range(1, n + 1):
|
||||
res += f"({i}, {j}), "
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
n = 5
|
||||
res = for_loop(n)
|
||||
print(f"\nfor ループの合計結果 res = {res}")
|
||||
print(f"\nfor ループで求めた和 res = {res}")
|
||||
|
||||
res = while_loop(n)
|
||||
print(f"\nwhile ループの合計結果 res = {res}")
|
||||
print(f"\nwhile ループで求めた和 res = {res}")
|
||||
|
||||
res = while_loop_ii(n)
|
||||
print(f"\nwhile ループ(2 回更新)の合計結果 res = {res}")
|
||||
print(f"\nwhile ループ(2 回の更新)で求めた和 res = {res}")
|
||||
|
||||
res = nested_for_loop(n)
|
||||
print(f"\n二重 for ループの走査結果 {res}")
|
||||
|
||||
@@ -10,24 +10,24 @@ 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
|
||||
@@ -47,23 +47,23 @@ 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) を再帰的に呼び出す
|
||||
# 再帰呼び出し 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}")
|
||||
print(f"\n再帰関数で求めた和 res = {res}")
|
||||
|
||||
res = for_loop_recur(n)
|
||||
print(f"\n反復で再帰をシミュレートした合計結果 res = {res}")
|
||||
print(f"\n反復で再帰を再現して求めた和 res = {res}")
|
||||
|
||||
res = tail_recur(n, 0)
|
||||
print(f"\n末尾再帰関数の合計結果 res = {res}")
|
||||
print(f"\n末尾再帰関数で求めた和 res = {res}")
|
||||
|
||||
res = fib(n)
|
||||
print(f"\nフィボナッチ数列の第 {n} 項は {res}")
|
||||
|
||||
@@ -13,36 +13,36 @@ from modules import ListNode, TreeNode, print_tree
|
||||
|
||||
def function() -> int:
|
||||
"""関数"""
|
||||
# 何らかの処理を行う
|
||||
# 何らかの処理を実行する
|
||||
return 0
|
||||
|
||||
|
||||
def constant(n: int):
|
||||
"""定数階"""
|
||||
# 定数、変数、オブジェクトは O(1) の空間を占める
|
||||
"""定数オーダー"""
|
||||
# 定数、変数、オブジェクトは O(1) の領域を使用する
|
||||
a = 0
|
||||
nums = [0] * 10000
|
||||
node = ListNode(0)
|
||||
# ループ内の変数は O(1) の空間を占める
|
||||
# ループ内の変数は O(1) の領域を使用する
|
||||
for _ in range(n):
|
||||
c = 0
|
||||
# ループ内の関数は O(1) の空間を占める
|
||||
# ループ内の関数は O(1) の領域を使用する
|
||||
for _ in range(n):
|
||||
function()
|
||||
|
||||
|
||||
def linear(n: int):
|
||||
"""線形階"""
|
||||
# 長さ n のリストは O(n) の空間を使用
|
||||
"""線形オーダー"""
|
||||
# 長さ n のリストは O(n) の領域を使用する
|
||||
nums = [0] * n
|
||||
# 長さ n のハッシュテーブルは O(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
|
||||
@@ -50,13 +50,13 @@ def linear_recur(n: int):
|
||||
|
||||
|
||||
def quadratic(n: int):
|
||||
"""二乗階"""
|
||||
# 二次元リストは O(n^2) の空間を使用
|
||||
"""二次オーダー"""
|
||||
# 二次元リストは 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
|
||||
@@ -65,7 +65,7 @@ def quadratic_recur(n: int) -> int:
|
||||
|
||||
|
||||
def build_tree(n: int) -> TreeNode | None:
|
||||
"""指数時間(完全二分木の構築)"""
|
||||
"""指数オーダー(パーフェクト二分木の構築)"""
|
||||
if n == 0:
|
||||
return None
|
||||
root = TreeNode(0)
|
||||
@@ -74,15 +74,15 @@ def build_tree(n: int) -> TreeNode | None:
|
||||
return root
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
n = 5
|
||||
# 定数階
|
||||
# 定数オーダー
|
||||
constant(n)
|
||||
# 線形階
|
||||
# 線形オーダー
|
||||
linear(n)
|
||||
linear_recur(n)
|
||||
# 二乗階
|
||||
# 二次オーダー
|
||||
quadratic(n)
|
||||
quadratic_recur(n)
|
||||
# 指数オーダー
|
||||
|
||||
@@ -6,7 +6,7 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def constant(n: int) -> int:
|
||||
"""定数階"""
|
||||
"""定数オーダー"""
|
||||
count = 0
|
||||
size = 100000
|
||||
for _ in range(size):
|
||||
@@ -15,7 +15,7 @@ def constant(n: int) -> int:
|
||||
|
||||
|
||||
def linear(n: int) -> int:
|
||||
"""線形階"""
|
||||
"""線形オーダー"""
|
||||
count = 0
|
||||
for _ in range(n):
|
||||
count += 1
|
||||
@@ -23,18 +23,18 @@ def linear(n: int) -> int:
|
||||
|
||||
|
||||
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 の二乗に比例する
|
||||
# ループの回数はデータの大きさ n の二乗に比例する
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
count += 1
|
||||
@@ -42,26 +42,26 @@ def quadratic(n: int) -> int:
|
||||
|
||||
|
||||
def bubble_sort(nums: list[int]) -> int:
|
||||
"""二次時間(バブルソート)"""
|
||||
"""二次オーダー(バブルソート)"""
|
||||
count = 0 # カウンタ
|
||||
# 外側のループ:未ソート区間は [0, i]
|
||||
for i in range(len(nums) - 1, 0, -1):
|
||||
# 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
|
||||
# 内側のループ:未ソート区間 [0, i] の最大要素を区間の右端に移す
|
||||
for j in range(i):
|
||||
if nums[j] > nums[j + 1]:
|
||||
# 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 回の単位操作が含まれる
|
||||
count += 3 # 要素の交換は 3 回の単位操作からなる
|
||||
return count
|
||||
|
||||
|
||||
def exponential(n: int) -> int:
|
||||
"""指数時間(ループ実装)"""
|
||||
"""指数オーダー(ループによる実装)"""
|
||||
count = 0
|
||||
base = 1
|
||||
# 細胞は各ラウンドで 2 つに分裂し、数列 1, 2, 4, 8, ..., 2^(n-1) を形成する
|
||||
# 各回で細胞が 2 つに分裂し、数列 1, 2, 4, 8, ..., 2^(n-1) を形成する
|
||||
for _ in range(n):
|
||||
for _ in range(base):
|
||||
count += 1
|
||||
@@ -71,14 +71,14 @@ def exponential(n: int) -> int:
|
||||
|
||||
|
||||
def exp_recur(n: int) -> int:
|
||||
"""指数時間(再帰実装)"""
|
||||
"""指数オーダー(再帰による実装)"""
|
||||
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
|
||||
@@ -87,67 +87,67 @@ def logarithmic(n: int) -> int:
|
||||
|
||||
|
||||
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
|
||||
# 二つに分割すると、部分問題の規模は半分になる
|
||||
# 2 つに分割し、部分問題の大きさを半分にする
|
||||
count = linear_log_recur(n // 2) + linear_log_recur(n // 2)
|
||||
# 現在の部分問題には n 個の操作が含まれる
|
||||
# 現在の部分問題には n 回の操作が含まれる
|
||||
for _ in range(n):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def factorial_recur(n: int) -> int:
|
||||
"""階乗時間(再帰実装)"""
|
||||
"""階乗オーダー(再帰による実装)"""
|
||||
if n == 0:
|
||||
return 1
|
||||
count = 0
|
||||
# 1個から n 個に分裂
|
||||
# 1 個から n 個に分裂する
|
||||
for _ in range(n):
|
||||
count += factorial_recur(n - 1)
|
||||
return count
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# n を変えて実行し、各計算量で操作回数がどう変化するかを確認できる
|
||||
# n を変更して実行し、各計算量で操作回数がどのように変化するか確かめられる
|
||||
n = 8
|
||||
print("入力データサイズ n =", n)
|
||||
print("入力データの大きさ n =", n)
|
||||
|
||||
count = constant(n)
|
||||
print("定数時間の操作回数 =", count)
|
||||
print("定数オーダーの操作回数 =", count)
|
||||
|
||||
count = linear(n)
|
||||
print("線形時間の操作回数 =", count)
|
||||
print("線形オーダーの操作回数 =", count)
|
||||
count = array_traversal([0] * n)
|
||||
print("線形時間(配列走査)の操作回数 =", count)
|
||||
print("線形オーダー(配列の走査)の操作回数 =", count)
|
||||
|
||||
count = quadratic(n)
|
||||
print("二乗時間の操作回数 =", count)
|
||||
print("二次オーダーの操作回数 =", count)
|
||||
nums = [i for i in range(n, 0, -1)] # [n, n-1, ..., 2, 1]
|
||||
count = bubble_sort(nums)
|
||||
print("二乗時間(バブルソート)の操作回数 =", count)
|
||||
print("二次オーダー(バブルソート)の操作回数 =", count)
|
||||
|
||||
count = exponential(n)
|
||||
print("指数時間(ループ実装)の操作回数 =", count)
|
||||
print("指数オーダー(ループによる実装)の操作回数 =", count)
|
||||
count = exp_recur(n)
|
||||
print("指数時間(再帰実装)の操作回数 =", count)
|
||||
print("指数オーダー(再帰による実装)の操作回数 =", count)
|
||||
|
||||
count = logarithmic(n)
|
||||
print("対数時間(ループ実装)の操作回数 =", count)
|
||||
print("対数オーダー(ループによる実装)の操作回数 =", count)
|
||||
count = log_recur(n)
|
||||
print("対数時間(再帰実装)の操作回数 =", count)
|
||||
print("対数オーダー(再帰による実装)の操作回数 =", count)
|
||||
|
||||
count = linear_log_recur(n)
|
||||
print("線形対数時間(再帰実装)の操作回数 =", count)
|
||||
print("線形対数オーダー(再帰による実装)の操作回数 =", count)
|
||||
|
||||
count = factorial_recur(n)
|
||||
print("階乗時間(再帰実装)の操作回数 =", count)
|
||||
print("階乗オーダー(再帰による実装)の操作回数 =", count)
|
||||
|
||||
@@ -8,29 +8,29 @@ import random
|
||||
|
||||
|
||||
def random_numbers(n: int) -> list[int]:
|
||||
"""要素が 1, 2, ..., n で順序がシャッフルされた配列を生成する"""
|
||||
"""要素が 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:
|
||||
"""配列 nums 内で数値 1 のインデックスを探す"""
|
||||
"""配列 nums 内で数値 1 のインデックスを探索する"""
|
||||
for i in range(len(nums)):
|
||||
# 要素 1 が配列の先頭にあるとき、最良時間計算量 O(1) となる
|
||||
# 要素 1 が配列の末尾にあるとき、最悪時間計算量 O(n) となる
|
||||
# 要素 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("\n配列 [ 1, 2, ..., n ] をシャッフルした結果 =", nums)
|
||||
print("数値 1 のインデックスは", index)
|
||||
|
||||
@@ -7,10 +7,10 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
def dfs(nums: list[int], target: int, i: int, j: int) -> int:
|
||||
"""二分探索:問題 f(i, j)"""
|
||||
# 区間が空なら対象要素は存在しないので -1 を返す
|
||||
# 区間が空なら目的の要素が存在しないため、-1 を返す
|
||||
if i > j:
|
||||
return -1
|
||||
# 中点インデックス m を計算
|
||||
# 中央のインデックス m を計算する
|
||||
m = (i + j) // 2
|
||||
if nums[m] < target:
|
||||
# 部分問題 f(m+1, j) を再帰的に解く
|
||||
@@ -19,7 +19,7 @@ def dfs(nums: list[int], target: int, i: int, j: int) -> int:
|
||||
# 部分問題 f(i, m-1) を再帰的に解く
|
||||
return dfs(nums, target, i, m - 1)
|
||||
else:
|
||||
# 目標要素が見つかったらそのインデックスを返す
|
||||
# 目的の要素が見つかったので、そのインデックスを返す
|
||||
return m
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ def binary_search(nums: list[int], target: int) -> int:
|
||||
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)
|
||||
print("目的の要素 6 のインデックス = ", index)
|
||||
|
||||
@@ -18,37 +18,37 @@ def dfs(
|
||||
l: int,
|
||||
r: int,
|
||||
) -> TreeNode | None:
|
||||
"""二分木を構築:分割統治"""
|
||||
# 部分木区間が空なら終了する
|
||||
"""二分木の構築:分割統治法"""
|
||||
# 部分木の区間が空の場合、終了する
|
||||
if r - l < 0:
|
||||
return None
|
||||
# ルートノードを初期化する
|
||||
# 根ノードを初期化する
|
||||
root = TreeNode(preorder[i])
|
||||
# m を求めて左右部分木を分割する
|
||||
# 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}")
|
||||
print(f"行きがけ順走査 = {preorder}")
|
||||
print(f"通りがけ順走査 = {inorder}")
|
||||
|
||||
root = build_tree(preorder, inorder)
|
||||
print("構築した二分木:")
|
||||
print("構築した二分木:")
|
||||
print_tree(root)
|
||||
|
||||
@@ -6,7 +6,7 @@ Author: Hello Algo Team
|
||||
|
||||
|
||||
def fast_pow(x: int, n: int) -> int:
|
||||
"""高速べき乗"""
|
||||
"""高速べき乗法"""
|
||||
if n == 0:
|
||||
return 1
|
||||
half = fast_pow(x, n // 2)
|
||||
|
||||
@@ -6,48 +6,48 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def move(src: list[int], tar: list[int]):
|
||||
"""円盤を 1 枚移動"""
|
||||
# src の上から円盤を1枚取り出す
|
||||
"""円盤を 1 枚移動する"""
|
||||
# src の一番上から円盤を 1 枚取り出す
|
||||
pan = src.pop()
|
||||
# 円盤を tar の上に置く
|
||||
# tar の一番上に円盤を置く
|
||||
tar.append(pan)
|
||||
|
||||
|
||||
def dfs(i: int, src: list[int], buf: list[int], tar: list[int]):
|
||||
"""ハノイの塔の問題 f(i) を解く"""
|
||||
# src に円盤が 1 枚だけ残っている場合は、そのまま tar へ移す
|
||||
# src に円盤が 1 枚だけ残っている場合、そのまま tar に移す
|
||||
if i == 1:
|
||||
move(src, tar)
|
||||
return
|
||||
# 部分問題 f(i-1):src の上部 i-1 枚の円盤を tar を補助にして buf へ移す
|
||||
# 部分問題 f(i-1) :src の上から i-1 枚の円盤を、tar を使って buf に移す
|
||||
dfs(i - 1, src, tar, buf)
|
||||
# 部分問題 f(1):src に残る 1 枚の円盤を tar に移す
|
||||
# 部分問題 f(1) :src に残った 1 枚の円盤を tar に移す
|
||||
move(src, tar)
|
||||
# 部分問題 f(i-1):buf の上部 i-1 枚の円盤を src を補助にして tar へ移す
|
||||
# 部分問題 f(i-1) :buf の上から i-1 枚の円盤を、src を使って tar に移す
|
||||
dfs(i - 1, buf, src, tar)
|
||||
|
||||
|
||||
def solve_hanota(A: list[int], B: list[int], C: list[int]):
|
||||
"""ハノイの塔を解く"""
|
||||
"""ハノイの塔の問題を解く"""
|
||||
n = len(A)
|
||||
# A の上から n 枚の円盤を B を介して C へ移す
|
||||
# A の上から n 枚の円盤を、B を使って C に移す
|
||||
dfs(n, A, B, C)
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# リスト末尾が柱の頂上
|
||||
# リストの末尾が棒の一番上に対応する
|
||||
A = [5, 4, 3, 2, 1]
|
||||
B = []
|
||||
C = []
|
||||
print("初期状態:")
|
||||
print("初期状態:")
|
||||
print(f"A = {A}")
|
||||
print(f"B = {B}")
|
||||
print(f"C = {C}")
|
||||
|
||||
solve_hanota(A, B, C)
|
||||
|
||||
print("円盤の移動完了後:")
|
||||
print("円盤の移動完了後:")
|
||||
print(f"A = {A}")
|
||||
print(f"B = {B}")
|
||||
print(f"C = {C}")
|
||||
|
||||
@@ -7,31 +7,31 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
def backtrack(choices: list[int], state: int, n: int, res: list[int]) -> int:
|
||||
"""バックトラッキング"""
|
||||
# 第 n 段に到達したら、方法数を 1 増やす
|
||||
# n 段目に到達したら、方法の数を 1 増やす
|
||||
if state == n:
|
||||
res[0] += 1
|
||||
# すべての選択肢を走査
|
||||
# すべての選択肢を走査する
|
||||
for choice in choices:
|
||||
# 枝刈り: 第 n 段を超えないようにする
|
||||
# 枝刈り: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] を使って方法数を記録する
|
||||
"""階段の上り方:バックトラッキング"""
|
||||
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} 通りです")
|
||||
print(f"{n} 段の階段を上る方法は全部で {res} 通り")
|
||||
|
||||
@@ -6,24 +6,24 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def climbing_stairs_constraint_dp(n: int) -> int:
|
||||
"""制約付き階段登り:動的計画法"""
|
||||
"""制約付きの階段の上り方:動的計画法"""
|
||||
if n == 1 or n == 2:
|
||||
return 1
|
||||
# 部分問題の解を保存するために dp テーブルを初期化
|
||||
# 部分問題の解を格納する 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} 通りです")
|
||||
print(f"{n} 段の階段を上る方法は全部で {res} 通り")
|
||||
|
||||
@@ -6,8 +6,8 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def dfs(i: int) -> int:
|
||||
"""検索"""
|
||||
# dp[1] と dp[2] は既知なので返す
|
||||
"""探索"""
|
||||
# dp[1] と dp[2] は既知なので、その値を返す
|
||||
if i == 1 or i == 2:
|
||||
return i
|
||||
# dp[i] = dp[i-1] + dp[i-2]
|
||||
@@ -16,13 +16,13 @@ def dfs(i: int) -> int:
|
||||
|
||||
|
||||
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} 通りです")
|
||||
print(f"{n} 段の階段を上る方法は全部で {res} 通り")
|
||||
|
||||
@@ -7,10 +7,10 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
def dfs(i: int, mem: list[int]) -> int:
|
||||
"""メモ化探索"""
|
||||
# dp[1] と dp[2] は既知なので返す
|
||||
# dp[1] と dp[2] は既知なので、その値を返す
|
||||
if i == 1 or i == 2:
|
||||
return i
|
||||
# dp[i] の記録があれば、それをそのまま返す
|
||||
# dp[i] が記録済みなら、その値をそのまま返す
|
||||
if mem[i] != -1:
|
||||
return mem[i]
|
||||
# dp[i] = dp[i-1] + dp[i-2]
|
||||
@@ -21,15 +21,15 @@ def dfs(i: int, mem: list[int]) -> int:
|
||||
|
||||
|
||||
def climbing_stairs_dfs_mem(n: int) -> int:
|
||||
"""階段登り:メモ化探索"""
|
||||
# mem[i] は第 i 段まで上る方法の総数を記録し、-1 は未記録を表す
|
||||
"""階段の上り方:メモ化探索"""
|
||||
# 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} 通りです")
|
||||
print(f"{n} 段の階段を上る方法は全部で {res} 通り")
|
||||
|
||||
@@ -6,21 +6,21 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def climbing_stairs_dp(n: int) -> int:
|
||||
"""階段登り:動的計画法"""
|
||||
"""階段の上り方:動的計画法"""
|
||||
if n == 1 or n == 2:
|
||||
return n
|
||||
# 部分問題の解を保存するために dp テーブルを初期化
|
||||
# 部分問題の解を格納する 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
|
||||
@@ -29,12 +29,12 @@ def climbing_stairs_dp_comp(n: int) -> int:
|
||||
return b
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
n = 9
|
||||
|
||||
res = climbing_stairs_dp(n)
|
||||
print(f"{n} 段の階段を上る方法は全部で {res} 通りです")
|
||||
print(f"{n} 段の階段を上る方法は全部で {res} 通り")
|
||||
|
||||
res = climbing_stairs_dp_comp(n)
|
||||
print(f"{n} 段の階段を上る方法は全部で {res} 通りです")
|
||||
print(f"{n} 段の階段を上る方法は全部で {res} 通り")
|
||||
|
||||
@@ -6,31 +6,31 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def coin_change_dp(coins: list[int], amt: int) -> int:
|
||||
"""コイン両替:動的計画法"""
|
||||
"""硬貨の両替:動的計画法"""
|
||||
n = len(coins)
|
||||
MAX = amt + 1
|
||||
# dp テーブルを初期化
|
||||
# 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 は選ばない
|
||||
# 目標金額を超える場合、硬貨 i を選ばない
|
||||
dp[i][a] = dp[i - 1][a]
|
||||
else:
|
||||
# 硬貨 i を選ばない場合と選ぶ場合の小さい方
|
||||
# 硬貨 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 テーブルを初期化する
|
||||
dp = [MAX] * (amt + 1)
|
||||
dp[0] = 0
|
||||
# 状態遷移
|
||||
@@ -38,23 +38,23 @@ def coin_change_dp_comp(coins: list[int], amt: int) -> int:
|
||||
# 順方向に走査する
|
||||
for a in range(1, amt + 1):
|
||||
if coins[i - 1] > a:
|
||||
# 目標金額を超えるなら硬貨 i は選ばない
|
||||
# 目標金額を超える場合、硬貨 i を選ばない
|
||||
dp[a] = dp[a]
|
||||
else:
|
||||
# 硬貨 i を選ばない場合と選ぶ場合の小さい方
|
||||
# 硬貨 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}")
|
||||
print(f"目標金額にするために必要な硬貨の最小枚数は {res}")
|
||||
|
||||
# 空間最適化後の動的計画法
|
||||
# 空間を最適化した動的計画法
|
||||
res = coin_change_dp_comp(coins, amt)
|
||||
print(f"目標金額を作るのに必要な最小硬貨枚数は {res}")
|
||||
print(f"目標金額にするために必要な硬貨の最小枚数は {res}")
|
||||
|
||||
@@ -6,9 +6,9 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def coin_change_ii_dp(coins: list[int], amt: int) -> int:
|
||||
"""コイン両替 II:動的計画法"""
|
||||
"""硬貨の両替 II:動的計画法"""
|
||||
n = len(coins)
|
||||
# dp テーブルを初期化
|
||||
# dp テーブルを初期化する
|
||||
dp = [[0] * (amt + 1) for _ in range(n + 1)]
|
||||
# 先頭列を初期化する
|
||||
for i in range(n + 1):
|
||||
@@ -17,18 +17,18 @@ def coin_change_ii_dp(coins: list[int], amt: int) -> int:
|
||||
for i in range(1, n + 1):
|
||||
for a in range(1, amt + 1):
|
||||
if coins[i - 1] > a:
|
||||
# 目標金額を超えるなら硬貨 i は選ばない
|
||||
# 目標金額を超える場合、硬貨 i を選ばない
|
||||
dp[i][a] = dp[i - 1][a]
|
||||
else:
|
||||
# コイン i を選ばない場合と選ぶ場合の和
|
||||
# 硬貨 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:空間最適化した動的計画法"""
|
||||
"""硬貨の両替 II:空間を最適化した動的計画法"""
|
||||
n = len(coins)
|
||||
# dp テーブルを初期化
|
||||
# dp テーブルを初期化する
|
||||
dp = [0] * (amt + 1)
|
||||
dp[0] = 1
|
||||
# 状態遷移
|
||||
@@ -36,23 +36,23 @@ def coin_change_ii_dp_comp(coins: list[int], amt: int) -> int:
|
||||
# 順方向に走査する
|
||||
for a in range(1, amt + 1):
|
||||
if coins[i - 1] > a:
|
||||
# 目標金額を超えるなら硬貨 i は選ばない
|
||||
# 目標金額を超える場合、硬貨 i を選ばない
|
||||
dp[a] = dp[a]
|
||||
else:
|
||||
# コイン i を選ばない場合と選ぶ場合の和
|
||||
# 硬貨 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}")
|
||||
print(f"目標金額になる硬貨の組み合わせの数は {res}")
|
||||
|
||||
# 空間最適化後の動的計画法
|
||||
# 空間を最適化した動的計画法
|
||||
res = coin_change_ii_dp_comp(coins, amt)
|
||||
print(f"目標金額を作る硬貨の組み合わせ数は {res}")
|
||||
print(f"目標金額になる硬貨の組み合わせの数は {res}")
|
||||
|
||||
@@ -6,20 +6,20 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def edit_distance_dfs(s: str, t: str, i: int, j: int) -> int:
|
||||
"""編集距離:総当たり探索"""
|
||||
# s と t がともに空なら 0 を返す
|
||||
"""編集距離:全探索"""
|
||||
# s と t がともに空の場合、0 を返す
|
||||
if i == 0 and j == 0:
|
||||
return 0
|
||||
# s が空なら t の長さを返す
|
||||
# s が空の場合、t の長さを返す
|
||||
if i == 0:
|
||||
return j
|
||||
# t が空なら s の長さを返す
|
||||
# t が空の場合、s の長さを返す
|
||||
if j == 0:
|
||||
return i
|
||||
# 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
# 2 つの文字が等しい場合、その 2 文字をスキップする
|
||||
if s[i - 1] == t[j - 1]:
|
||||
return edit_distance_dfs(s, t, i - 1, j - 1)
|
||||
# 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
# 最小編集回数 = 挿入、削除、置換の 3 操作での最小編集回数 + 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)
|
||||
@@ -29,22 +29,22 @@ def edit_distance_dfs(s: str, t: str, i: int, j: int) -> int:
|
||||
|
||||
def edit_distance_dfs_mem(s: str, t: str, mem: list[list[int]], i: int, j: int) -> int:
|
||||
"""編集距離:メモ化探索"""
|
||||
# s と t がともに空なら 0 を返す
|
||||
# s と t がともに空の場合、0 を返す
|
||||
if i == 0 and j == 0:
|
||||
return 0
|
||||
# s が空なら t の長さを返す
|
||||
# s が空の場合、t の長さを返す
|
||||
if i == 0:
|
||||
return j
|
||||
# t が空なら s の長さを返す
|
||||
# t が空の場合、s の長さを返す
|
||||
if j == 0:
|
||||
return i
|
||||
# 記録済みなら、それをそのまま返す
|
||||
# 記録済みなら、その値をそのまま返す
|
||||
if mem[i][j] != -1:
|
||||
return mem[i][j]
|
||||
# 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
# 2 つの文字が等しい場合、その 2 文字をスキップする
|
||||
if s[i - 1] == t[j - 1]:
|
||||
return edit_distance_dfs_mem(s, t, mem, i - 1, j - 1)
|
||||
# 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
# 最小編集回数 = 挿入、削除、置換の 3 操作での最小編集回数 + 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)
|
||||
@@ -62,20 +62,20 @@ def edit_distance_dp(s: str, t: str) -> int:
|
||||
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]:
|
||||
# 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
# 2 つの文字が等しい場合、その 2 文字をスキップする
|
||||
dp[i][j] = dp[i - 1][j - 1]
|
||||
else:
|
||||
# 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
# 最小編集回数 = 挿入、削除、置換の 3 操作での最小編集回数 + 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)
|
||||
# 状態遷移:先頭行
|
||||
@@ -90,16 +90,16 @@ def edit_distance_dp_comp(s: str, t: str) -> int:
|
||||
for j in range(1, m + 1):
|
||||
temp = dp[j]
|
||||
if s[i - 1] == t[j - 1]:
|
||||
# 2 つの文字が等しければ、その 2 文字をそのままスキップする
|
||||
# 2 つの文字が等しい場合、その 2 文字をスキップする
|
||||
dp[j] = leftup
|
||||
else:
|
||||
# 最小編集回数 = 挿入・削除・置換の 3 操作における最小編集回数 + 1
|
||||
# 最小編集回数 = 挿入、削除、置換の 3 操作での最小編集回数 + 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"
|
||||
@@ -107,17 +107,17 @@ if __name__ == "__main__":
|
||||
|
||||
# 全探索
|
||||
res = edit_distance_dfs(s, t, n, m)
|
||||
print(f"{s} を {t} に変更するには最小で {res} 回の編集が必要です")
|
||||
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} 回の編集が必要です")
|
||||
print(f"{s} を {t} に変更するには最低 {res} 回の編集が必要")
|
||||
|
||||
# 動的計画法
|
||||
res = edit_distance_dp(s, t)
|
||||
print(f"{s} を {t} に変更するには最小で {res} 回の編集が必要です")
|
||||
print(f"{s} を {t} に変更するには最低 {res} 回の編集が必要")
|
||||
|
||||
# 空間最適化後の動的計画法
|
||||
# 空間を最適化した動的計画法
|
||||
res = edit_distance_dp_comp(s, t)
|
||||
print(f"{s} を {t} に変更するには最小で {res} 回の編集が必要です")
|
||||
print(f"{s} を {t} に変更するには最低 {res} 回の編集が必要")
|
||||
|
||||
@@ -6,77 +6,77 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def knapsack_dfs(wgt: list[int], val: list[int], i: int, c: int) -> int:
|
||||
"""0-1 ナップサック:総当たり探索"""
|
||||
# すべての品物を選び終えたか、ナップサックに残り容量がなければ、価値 0 を返す
|
||||
"""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]
|
||||
# 2つの案のうち価値が大きいほうを返す
|
||||
# 2 通りの選択のうち、価値が大きいほうを返す
|
||||
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 を返す
|
||||
"""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]
|
||||
# 2 つの案のうち価値が大きい方を記録して返す
|
||||
# 2 通りの選択のうち、価値が大きいほうを記録して返す
|
||||
mem[i][c] = max(no, yes)
|
||||
return mem[i][c]
|
||||
|
||||
|
||||
def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int:
|
||||
"""0-1 ナップサック:動的計画法"""
|
||||
"""0-1 ナップサック問題:動的計画法"""
|
||||
n = len(wgt)
|
||||
# dp テーブルを初期化
|
||||
# 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 は選ばない
|
||||
# ナップサックの容量を超える場合、品物 i を選ばない
|
||||
dp[i][c] = dp[i - 1][c]
|
||||
else:
|
||||
# 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
# 品物 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 ナップサック:空間最適化後の動的計画法"""
|
||||
"""0-1 ナップサック問題:空間を最適化した動的計画法"""
|
||||
n = len(wgt)
|
||||
# dp テーブルを初期化
|
||||
# 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 は選ばない
|
||||
# ナップサックの容量を超える場合、品物 i を選ばない
|
||||
dp[c] = dp[c]
|
||||
else:
|
||||
# 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
# 品物 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]
|
||||
@@ -85,17 +85,17 @@ if __name__ == "__main__":
|
||||
|
||||
# 全探索
|
||||
res = knapsack_dfs(wgt, val, n, cap)
|
||||
print(f"ナップサック容量を超えない最大価値は {res}")
|
||||
print(f"ナップサックの容量を超えない品物の最大価値は {res}")
|
||||
|
||||
# メモ化探索
|
||||
mem = [[-1] * (cap + 1) for _ in range(n + 1)]
|
||||
res = knapsack_dfs_mem(wgt, val, mem, n, cap)
|
||||
print(f"ナップサック容量を超えない最大価値は {res}")
|
||||
print(f"ナップサックの容量を超えない品物の最大価値は {res}")
|
||||
|
||||
# 動的計画法
|
||||
res = knapsack_dp(wgt, val, cap)
|
||||
print(f"ナップサック容量を超えない最大価値は {res}")
|
||||
print(f"ナップサックの容量を超えない品物の最大価値は {res}")
|
||||
|
||||
# 空間最適化後の動的計画法
|
||||
# 空間を最適化した動的計画法
|
||||
res = knapsack_dp_comp(wgt, val, cap)
|
||||
print(f"ナップサック容量を超えない最大価値は {res}")
|
||||
print(f"ナップサックの容量を超えない品物の最大価値は {res}")
|
||||
|
||||
@@ -6,22 +6,22 @@ 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 テーブルを初期化する
|
||||
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]
|
||||
@@ -31,13 +31,13 @@ def min_cost_climbing_stairs_dp_comp(cost: list[int]) -> int:
|
||||
return b
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1]
|
||||
print(f"入力された階段コストのリストは {cost}")
|
||||
print(f"入力した階段のコストのリストは {cost}")
|
||||
|
||||
res = min_cost_climbing_stairs_dp(cost)
|
||||
print(f"階段を上り切る最小コストは {res}")
|
||||
print(f"階段を上り終える最小コストは {res}")
|
||||
|
||||
res = min_cost_climbing_stairs_dp_comp(cost)
|
||||
print(f"階段を上り切る最小コストは {res}")
|
||||
print(f"階段を上り終える最小コストは {res}")
|
||||
|
||||
@@ -12,13 +12,13 @@ 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) までの最小経路コストを返す
|
||||
# 左上から (i, j) までの最小経路コストを返す
|
||||
return min(left, up) + grid[i][j]
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ def min_path_sum_dfs_mem(
|
||||
# 左上のセルなら探索を終了する
|
||||
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) までの最小経路コストを記録して返す
|
||||
@@ -46,7 +46,7 @@ def min_path_sum_dfs_mem(
|
||||
def min_path_sum_dp(grid: list[list[int]]) -> int:
|
||||
"""最小経路和:動的計画法"""
|
||||
n, m = len(grid), len(grid[0])
|
||||
# dp テーブルを初期化
|
||||
# dp テーブルを初期化する
|
||||
dp = [[0] * m for _ in range(n)]
|
||||
dp[0][0] = grid[0][0]
|
||||
# 状態遷移:先頭行
|
||||
@@ -55,7 +55,7 @@ def min_path_sum_dp(grid: list[list[int]]) -> int:
|
||||
# 状態遷移:先頭列
|
||||
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]
|
||||
@@ -63,9 +63,9 @@ def min_path_sum_dp(grid: list[list[int]]) -> int:
|
||||
|
||||
|
||||
def min_path_sum_dp_comp(grid: list[list[int]]) -> int:
|
||||
"""最小経路和:空間最適化後の動的計画法"""
|
||||
"""最小経路和:空間を最適化した動的計画法"""
|
||||
n, m = len(grid), len(grid[0])
|
||||
# dp テーブルを初期化
|
||||
# dp テーブルを初期化する
|
||||
dp = [0] * m
|
||||
# 状態遷移:先頭行
|
||||
dp[0] = grid[0][0]
|
||||
@@ -81,7 +81,7 @@ def min_path_sum_dp_comp(grid: list[list[int]]) -> int:
|
||||
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])
|
||||
@@ -99,6 +99,6 @@ if __name__ == "__main__":
|
||||
res = min_path_sum_dp(grid)
|
||||
print(f"左上から右下までの最小経路和は {res}")
|
||||
|
||||
# 空間最適化後の動的計画法
|
||||
# 空間を最適化した動的計画法
|
||||
res = min_path_sum_dp_comp(grid)
|
||||
print(f"左上から右下までの最小経路和は {res}")
|
||||
|
||||
@@ -6,41 +6,41 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def unbounded_knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int:
|
||||
"""完全ナップサック問題:動的計画法"""
|
||||
"""個数制限なしナップサック問題:動的計画法"""
|
||||
n = len(wgt)
|
||||
# dp テーブルを初期化
|
||||
# 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 は選ばない
|
||||
# ナップサックの容量を超える場合、品物 i を選ばない
|
||||
dp[i][c] = dp[i - 1][c]
|
||||
else:
|
||||
# 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
# 品物 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 テーブルを初期化する
|
||||
dp = [0] * (cap + 1)
|
||||
# 状態遷移
|
||||
for i in range(1, n + 1):
|
||||
# 順方向に走査する
|
||||
for c in range(1, cap + 1):
|
||||
if wgt[i - 1] > c:
|
||||
# ナップサック容量を超えるなら品物 i は選ばない
|
||||
# ナップサックの容量を超える場合、品物 i を選ばない
|
||||
dp[c] = dp[c]
|
||||
else:
|
||||
# 品物 i を選ばない場合と選ぶ場合の大きい方
|
||||
# 品物 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]
|
||||
@@ -48,8 +48,8 @@ if __name__ == "__main__":
|
||||
|
||||
# 動的計画法
|
||||
res = unbounded_knapsack_dp(wgt, val, cap)
|
||||
print(f"ナップサック容量を超えない最大価値は {res}")
|
||||
print(f"ナップサックの容量を超えない品物の最大価値は {res}")
|
||||
|
||||
# 空間最適化後の動的計画法
|
||||
# 空間を最適化した動的計画法
|
||||
res = unbounded_knapsack_dp_comp(wgt, val, cap)
|
||||
print(f"ナップサック容量を超えない最大価値は {res}")
|
||||
print(f"ナップサックの容量を超えない品物の最大価値は {res}")
|
||||
|
||||
@@ -12,67 +12,67 @@ from modules import Vertex, vals_to_vets
|
||||
|
||||
|
||||
class GraphAdjList:
|
||||
"""隣接リストに基づく無向グラフクラス"""
|
||||
"""隣接リストで実装した無向グラフクラス"""
|
||||
|
||||
def __init__(self, edges: list[list[Vertex]]):
|
||||
"""コンストラクタ"""
|
||||
# 隣接リスト。key は頂点、value はその頂点に隣接する全頂点
|
||||
# 隣接リスト。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 を追加
|
||||
# 辺 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 を削除
|
||||
# 辺 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 に対応するリストを削除
|
||||
# 隣接リストから頂点 vet に対応する連結リストを削除する
|
||||
self.adj_list.pop(vet)
|
||||
# 他の頂点のリストを走査し、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]],
|
||||
@@ -83,29 +83,29 @@ if __name__ == "__main__":
|
||||
[v[3], v[4]],
|
||||
]
|
||||
graph = GraphAdjList(edges)
|
||||
print("\n初期化後、グラフは")
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 辺を追加する
|
||||
# 頂点 1, 2 は `v[0]`, `v[2]`
|
||||
# 頂点 1, 2 は v[0], v[2] に対応する
|
||||
graph.add_edge(v[0], v[2])
|
||||
print("\n辺 1-2 を追加した後、グラフは")
|
||||
print("\n辺 1-2 を追加した後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 辺を削除する
|
||||
# 頂点 1, 3 はそれぞれ v[0], v[1]
|
||||
# 頂点 1, 3 は v[0], v[1] に対応する
|
||||
graph.remove_edge(v[0], v[1])
|
||||
print("\n辺 1-3 を削除した後、グラフは")
|
||||
print("\n辺 1-3 を削除した後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 頂点を追加
|
||||
# 頂点を追加する
|
||||
v5 = Vertex(6)
|
||||
graph.add_vertex(v5)
|
||||
print("\n頂点 6 を追加した後、グラフは")
|
||||
print("\n頂点 6 を追加した後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 頂点を削除する
|
||||
# 頂点 3 は v[1]
|
||||
# 頂点 3 は v[1] に対応する
|
||||
graph.remove_vertex(v[1])
|
||||
print("\n頂点 3 を削除した後、グラフは")
|
||||
print("\n頂点 3 を削除した後のグラフ")
|
||||
graph.print()
|
||||
|
||||
@@ -12,105 +12,105 @@ 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 の要素インデックスに対応する
|
||||
# 辺を追加する
|
||||
# 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)
|
||||
# 隣接行列に 1 行追加
|
||||
# 隣接行列に 1 行追加する
|
||||
new_row = [0] * n
|
||||
self.adj_mat.append(new_row)
|
||||
# 隣接行列に 1 列追加
|
||||
# 隣接行列に 1 列追加する
|
||||
for row in self.adj_mat:
|
||||
row.append(0)
|
||||
|
||||
def remove_vertex(self, index: int):
|
||||
"""頂点を削除"""
|
||||
"""頂点を削除する"""
|
||||
if index >= self.size():
|
||||
raise IndexError()
|
||||
# 頂点リストから index の頂点を削除する
|
||||
# 頂点リストからインデックス index の頂点を削除する
|
||||
self.vertices.pop(index)
|
||||
# 隣接行列で index 行を削除する
|
||||
# 隣接行列からインデックス index の行を削除する
|
||||
self.adj_mat.pop(index)
|
||||
# 隣接行列で index 列を削除する
|
||||
# 隣接行列からインデックス index の列を削除する
|
||||
for row in self.adj_mat:
|
||||
row.pop(index)
|
||||
|
||||
def add_edge(self, i: int, j: int):
|
||||
"""辺を追加"""
|
||||
# パラメータ i, j は vertices の要素インデックスに対応する
|
||||
# 範囲外と同値の場合の処理
|
||||
"""辺を追加する"""
|
||||
# 引数 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) を満たす
|
||||
# 無向グラフの隣接行列は主対角線に関して対称であり、(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 の要素インデックスに対応する
|
||||
# 範囲外と同値の場合の処理
|
||||
"""辺を削除する"""
|
||||
# 引数 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 の要素インデックスに対応する
|
||||
# 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初期化後、グラフは")
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 辺を追加する
|
||||
# 頂点 1, 2 のインデックスはそれぞれ 0, 2
|
||||
graph.add_edge(0, 2)
|
||||
print("\n辺 1-2 を追加した後、グラフは")
|
||||
print("\n辺 1-2 を追加した後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 辺を削除する
|
||||
# 頂点 1, 3 のインデックスはそれぞれ 0, 1
|
||||
graph.remove_edge(0, 1)
|
||||
print("\n辺 1-3 を削除した後、グラフは")
|
||||
print("\n辺 1-3 を削除した後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 頂点を追加
|
||||
# 頂点を追加する
|
||||
graph.add_vertex(6)
|
||||
print("\n頂点 6 を追加した後、グラフは")
|
||||
print("\n頂点 6 を追加した後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 頂点を削除する
|
||||
# 頂点 3 のインデックスは 1
|
||||
graph.remove_vertex(1)
|
||||
print("\n頂点 3 を削除した後、グラフは")
|
||||
print("\n頂点 3 を削除した後のグラフ")
|
||||
graph.print()
|
||||
|
||||
@@ -14,31 +14,31 @@ from graph_adjacency_list import GraphAdjList
|
||||
|
||||
|
||||
def graph_bfs(graph: GraphAdjList, start_vet: Vertex) -> list[Vertex]:
|
||||
"""幅優先探索"""
|
||||
# 指定した頂点の隣接頂点をすべて取得できるよう、隣接リストでグラフを表現する
|
||||
"""幅優先走査"""
|
||||
# 指定した頂点に隣接する全頂点を取得しやすいように、グラフを隣接リストで表す
|
||||
# 頂点の走査順序
|
||||
res = []
|
||||
# 訪問済み頂点を記録するためのハッシュ集合
|
||||
# 訪問済みの頂点を記録するハッシュ集合
|
||||
visited = set[Vertex]([start_vet])
|
||||
# BFS の実装にキューを用いる
|
||||
# BFS の実装にキューを使う
|
||||
que = deque[Vertex]([start_vet])
|
||||
# 頂点 vet を起点に、すべての頂点を訪問し終えるまで繰り返す
|
||||
# 頂点 vet を始点として、すべての頂点を訪問するまで繰り返す
|
||||
while len(que) > 0:
|
||||
vet = que.popleft() # 先頭の頂点をデキュー
|
||||
res.append(vet) # 訪問した頂点を記録
|
||||
# この頂点のすべての隣接頂点を走査
|
||||
vet = que.popleft() # キューの先頭の頂点をデキューする
|
||||
res.append(vet) # 訪問した頂点を記録する
|
||||
# この頂点に隣接する全頂点を走査する
|
||||
for adj_vet in graph.adj_list[vet]:
|
||||
if adj_vet in visited:
|
||||
continue # 訪問済みの頂点をスキップ
|
||||
que.append(adj_vet) # 未訪問の頂点のみをキューに追加
|
||||
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]],
|
||||
@@ -55,10 +55,10 @@ if __name__ == "__main__":
|
||||
[v[7], v[8]],
|
||||
]
|
||||
graph = GraphAdjList(edges)
|
||||
print("\n初期化後、グラフは")
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 幅優先探索
|
||||
# 幅優先走査
|
||||
res = graph_bfs(graph, v[0])
|
||||
print("\n幅優先探索(BFS)の頂点順序は")
|
||||
print("\n幅優先走査(BFS)の頂点の順序")
|
||||
print(vets_to_vals(res))
|
||||
|
||||
@@ -14,30 +14,30 @@ from graph_adjacency_list import GraphAdjList
|
||||
|
||||
def dfs(graph: GraphAdjList, visited: set[Vertex], res: list[Vertex], vet: Vertex):
|
||||
"""深さ優先走査の補助関数"""
|
||||
res.append(vet) # 訪問した頂点を記録
|
||||
res.append(vet) # 訪問した頂点を記録する
|
||||
visited.add(vet) # この頂点を訪問済みにする
|
||||
# この頂点のすべての隣接頂点を走査
|
||||
# この頂点に隣接する全頂点を走査する
|
||||
for adjVet in graph.adj_list[vet]:
|
||||
if adjVet in visited:
|
||||
continue # 訪問済みの頂点をスキップ
|
||||
# 隣接頂点を再帰的に訪問
|
||||
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]],
|
||||
@@ -48,10 +48,10 @@ if __name__ == "__main__":
|
||||
[v[5], v[6]],
|
||||
]
|
||||
graph = GraphAdjList(edges)
|
||||
print("\n初期化後、グラフは")
|
||||
print("\n初期化後のグラフ")
|
||||
graph.print()
|
||||
|
||||
# 深さ優先探索
|
||||
# 深さ優先走査
|
||||
res = graph_dfs(graph, v[0])
|
||||
print("\n深さ優先探索(DFS)の頂点順序は")
|
||||
print("\n深さ優先走査(DFS)の頂点の順序")
|
||||
print(vets_to_vals(res))
|
||||
|
||||
@@ -6,43 +6,43 @@ 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] を選択する
|
||||
# coins[i] を選ぶ
|
||||
amt -= coins[i]
|
||||
count += 1
|
||||
# 実行可能な解が見つからなければ -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}")
|
||||
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")
|
||||
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")
|
||||
print(f"{amt} にするために必要な硬貨の最小枚数は {res}")
|
||||
print(f"実際に必要な最小枚数は 2 、すなわち 49 + 49")
|
||||
|
||||
@@ -14,12 +14,12 @@ class Item:
|
||||
|
||||
|
||||
def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int:
|
||||
"""分数ナップサック:貪欲法"""
|
||||
# 重さと価値の 2 属性を持つ品物リストを作成
|
||||
"""分割可能ナップサック問題:貪欲法"""
|
||||
# 重さと価値の 2 つの属性を持つ品物のリストを作成する
|
||||
items = [Item(w, v) for w, v in zip(wgt, val)]
|
||||
# 単位価値 item.v / item.w の高い順にソートする
|
||||
# 単位重さ当たりの価値 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:
|
||||
@@ -27,20 +27,20 @@ def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int:
|
||||
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}")
|
||||
print(f"ナップサックの容量を超えない品物の最大価値は {res}")
|
||||
|
||||
@@ -7,16 +7,16 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
def max_capacity(ht: list[int]) -> int:
|
||||
"""最大容量:貪欲法"""
|
||||
# i, j を初期化し、それぞれ配列の両端に置く
|
||||
# i, j を初期化し、配列の両端をそれぞれ指すようにする
|
||||
i, j = 0, len(ht) - 1
|
||||
# 初期の最大容量は 0
|
||||
# 最大容量の初期値は 0
|
||||
res = 0
|
||||
# 2 枚の板が出会うまで貪欲選択を繰り返す
|
||||
# 2 枚の板が出会うまで貪欲な選択を繰り返す
|
||||
while i < j:
|
||||
# 最大容量を更新する
|
||||
cap = min(ht[i], ht[j]) * (j - i)
|
||||
res = max(res, cap)
|
||||
# 短い方を内側へ動かす
|
||||
# 低い方の仕切り板を内側に移動する
|
||||
if ht[i] < ht[j]:
|
||||
i += 1
|
||||
else:
|
||||
@@ -24,10 +24,10 @@ def max_capacity(ht: list[int]) -> int:
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
ht = [3, 8, 5, 2, 7, 7, 3, 4]
|
||||
|
||||
# 貪欲法
|
||||
# 貪欲アルゴリズム
|
||||
res = max_capacity(ht)
|
||||
print(f"最大容量は {res}")
|
||||
|
||||
@@ -8,26 +8,26 @@ import math
|
||||
|
||||
|
||||
def max_product_cutting(n: int) -> int:
|
||||
"""最大切断積:貪欲法"""
|
||||
# n <= 3 のときは、必ず 1 を切り出す
|
||||
"""分割後の積の最大化:貪欲法"""
|
||||
# n <= 3 の場合、必ず 1 を 1 つ切り出す必要がある
|
||||
if n <= 3:
|
||||
return 1 * (n - 1)
|
||||
# 貪欲に 3 を切り出し、a を 3 の個数、b を余りとする
|
||||
# 貪欲に 3 を切り出す。a は 3 の個数、b は余り
|
||||
a, b = n // 3, n % 3
|
||||
if b == 1:
|
||||
# 余りが 1 のときは、1 * 3 を 2 * 2 に変える
|
||||
# 余りが 1 の場合、1 * 3 の組を 2 * 2 に変換する
|
||||
return int(math.pow(3, a - 1)) * 2 * 2
|
||||
if b == 2:
|
||||
# 余りが 2 のときは、そのままにする
|
||||
# 余りが 2 の場合、何もしない
|
||||
return int(math.pow(3, a)) * 2
|
||||
# 余りが 0 のときは、そのままにする
|
||||
# 余りが 0 の場合、何もしない
|
||||
return int(math.pow(3, a))
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
n = 58
|
||||
|
||||
# 貪欲法
|
||||
# 貪欲アルゴリズム
|
||||
res = max_product_cutting(n)
|
||||
print(f"最大分割積は {res}")
|
||||
print(f"分割後の積の最大値は {res}")
|
||||
|
||||
@@ -6,7 +6,7 @@ Author: msk397 (machangxinq@gmail.com)
|
||||
|
||||
|
||||
class Pair:
|
||||
"""キーと値の組"""
|
||||
"""キーと値のペア"""
|
||||
|
||||
def __init__(self, key: int, val: str):
|
||||
self.key = key
|
||||
@@ -14,11 +14,11 @@ class Pair:
|
||||
|
||||
|
||||
class ArrayHashMap:
|
||||
"""配列ベースのハッシュテーブル"""
|
||||
"""配列で実装したハッシュテーブル"""
|
||||
|
||||
def __init__(self):
|
||||
"""コンストラクタ"""
|
||||
# 100 個のバケットを含む配列を初期化
|
||||
# 100 個のバケットを含む配列を初期化する
|
||||
self.buckets: list[Pair | None] = [None] * 100
|
||||
|
||||
def hash_func(self, key: int) -> int:
|
||||
@@ -27,7 +27,7 @@ class ArrayHashMap:
|
||||
return index
|
||||
|
||||
def get(self, key: int) -> str | None:
|
||||
"""検索操作"""
|
||||
"""取得操作"""
|
||||
index: int = self.hash_func(key)
|
||||
pair: Pair = self.buckets[index]
|
||||
if pair is None:
|
||||
@@ -43,11 +43,11 @@ class ArrayHashMap:
|
||||
def remove(self, key: int):
|
||||
"""削除操作"""
|
||||
index: int = self.hash_func(key)
|
||||
# None に設定し、削除を表す
|
||||
# None に設定して、削除を表す
|
||||
self.buckets[index] = None
|
||||
|
||||
def entry_set(self) -> list[Pair]:
|
||||
"""すべてのキーと値のペアを取得"""
|
||||
"""キーと値のペアをすべて取得する"""
|
||||
result: list[Pair] = []
|
||||
for pair in self.buckets:
|
||||
if pair is not None:
|
||||
@@ -55,7 +55,7 @@ class ArrayHashMap:
|
||||
return result
|
||||
|
||||
def key_set(self) -> list[int]:
|
||||
"""すべてのキーを取得"""
|
||||
"""すべてのキーを取得する"""
|
||||
result = []
|
||||
for pair in self.buckets:
|
||||
if pair is not None:
|
||||
@@ -63,7 +63,7 @@ class ArrayHashMap:
|
||||
return result
|
||||
|
||||
def value_set(self) -> list[str]:
|
||||
"""すべての値を取得"""
|
||||
"""すべての値を取得する"""
|
||||
result = []
|
||||
for pair in self.buckets:
|
||||
if pair is not None:
|
||||
@@ -71,47 +71,47 @@ class ArrayHashMap:
|
||||
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追加完了後、ハッシュテーブルは\nKey -> Value")
|
||||
# ハッシュテーブルにキーと値のペア (key, value) を追加する
|
||||
hmap.put(12836, "小哈")
|
||||
hmap.put(15937, "小啰")
|
||||
hmap.put(16750, "小算")
|
||||
hmap.put(13276, "小法")
|
||||
hmap.put(10583, "小鸭")
|
||||
print("\n追加後のハッシュテーブル\nKey -> Value")
|
||||
hmap.print()
|
||||
|
||||
# 検索操作
|
||||
# ハッシュテーブルにキー key を入力し、値 value を取得する
|
||||
# 取得操作
|
||||
# ハッシュテーブルにキー key を渡し、値 value を取得する
|
||||
name = hmap.get(15937)
|
||||
print("\n学籍番号 15937 を入力すると、氏名は " + name)
|
||||
print("\n学生番号 15937 に対応する氏名は " + name)
|
||||
|
||||
# 削除操作
|
||||
# ハッシュテーブルからキーと値の組 (key, value) を削除する
|
||||
# ハッシュテーブルからキーと値のペア (key, value) を削除する
|
||||
hmap.remove(10583)
|
||||
print("\n10583 を削除した後、ハッシュテーブルは\nKey -> Value")
|
||||
print("\n10583 を削除した後のハッシュテーブル\nKey -> Value")
|
||||
hmap.print()
|
||||
|
||||
# ハッシュテーブルを走査
|
||||
print("\nキーと値のペア Key->Value を走査")
|
||||
# ハッシュテーブルを走査する
|
||||
print("\nキーと値のペア Key->Value を走査する")
|
||||
for pair in hmap.entry_set():
|
||||
print(pair.key, "->", pair.val)
|
||||
|
||||
print("\nキー Key を個別に走査")
|
||||
print("\nキー Key だけを走査する")
|
||||
for key in hmap.key_set():
|
||||
print(key)
|
||||
|
||||
print("\n値 Value を個別に走査")
|
||||
print("\n値 Value だけを走査する")
|
||||
for val in hmap.value_set():
|
||||
print(val)
|
||||
|
||||
@@ -10,7 +10,7 @@ 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)
|
||||
@@ -18,17 +18,17 @@ if __name__ == "__main__":
|
||||
|
||||
bol = True
|
||||
hash_bol = hash(bol)
|
||||
print(f"ブール値 {bol} のハッシュ値は {hash_bol}")
|
||||
print(f"真偽値 {bol} のハッシュ値は {hash_bol}")
|
||||
|
||||
dec = 3.14159
|
||||
hash_dec = hash(dec)
|
||||
print(f"小数 {dec} のハッシュ値は {hash_dec}")
|
||||
|
||||
str = "Hello アルゴリズム"
|
||||
str = "Hello 算法"
|
||||
hash_str = hash(str)
|
||||
print(f"文字列 {str} のハッシュ値は {hash_str}")
|
||||
|
||||
tup = (12836, "シャオハー")
|
||||
tup = (12836, "小哈")
|
||||
hash_tup = hash(tup)
|
||||
print(f"タプル {tup} のハッシュ値は {hash(hash_tup)}")
|
||||
|
||||
|
||||
@@ -10,41 +10,41 @@ 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追加完了後、ハッシュテーブルは\nKey -> Value")
|
||||
# ハッシュテーブルにキーと値のペア (key, value) を追加する
|
||||
hmap[12836] = "小哈"
|
||||
hmap[15937] = "小啰"
|
||||
hmap[16750] = "小算"
|
||||
hmap[13276] = "小法"
|
||||
hmap[10583] = "小鸭"
|
||||
print("\n追加後のハッシュテーブル\nKey -> Value")
|
||||
print_dict(hmap)
|
||||
|
||||
# 検索操作
|
||||
# ハッシュテーブルにキー key を入力し、値 value を取得する
|
||||
# 取得操作
|
||||
# ハッシュテーブルにキー key を渡し、値 value を取得する
|
||||
name: str = hmap[15937]
|
||||
print("\n学籍番号 15937 を入力すると、氏名は " + name)
|
||||
print("\n学生番号 15937 に対応する氏名は " + name)
|
||||
|
||||
# 削除操作
|
||||
# ハッシュテーブルからキーと値の組 (key, value) を削除する
|
||||
# ハッシュテーブルからキーと値のペア (key, value) を削除する
|
||||
hmap.pop(10583)
|
||||
print("\n10583 を削除した後、ハッシュテーブルは\nKey -> Value")
|
||||
print("\n10583 を削除した後のハッシュテーブル\nKey -> Value")
|
||||
print_dict(hmap)
|
||||
|
||||
# ハッシュテーブルを走査
|
||||
print("\nキーと値のペア Key->Value を走査")
|
||||
# ハッシュテーブルを走査する
|
||||
print("\nキーと値のペア Key->Value を走査する")
|
||||
for key, value in hmap.items():
|
||||
print(key, "->", value)
|
||||
|
||||
print("\nキー Key を個別に走査")
|
||||
print("\nキー Key だけを走査する")
|
||||
for key in hmap.keys():
|
||||
print(key)
|
||||
|
||||
print("\n値 Value を個別に走査")
|
||||
print("\n値 Value だけを走査する")
|
||||
for val in hmap.values():
|
||||
print(val)
|
||||
|
||||
@@ -12,14 +12,14 @@ 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.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:
|
||||
@@ -31,29 +31,29 @@ class HashMapChaining:
|
||||
return self.size / self.capacity
|
||||
|
||||
def get(self, key: int) -> str | None:
|
||||
"""検索操作"""
|
||||
"""取得操作"""
|
||||
index = self.hash_func(key)
|
||||
bucket = self.buckets[index]
|
||||
# バケットを走査し、key が見つかれば対応する val を返す
|
||||
# バケットを走査し、key が見つかったら対応する val を返す
|
||||
for pair in bucket:
|
||||
if pair.key == key:
|
||||
return pair.val
|
||||
# key が見つからない場合は None を返す
|
||||
# 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 を更新して返す
|
||||
# バケットを走査し、指定された key が見つかったら、対応する val を更新して戻る
|
||||
for pair in bucket:
|
||||
if pair.key == key:
|
||||
pair.val = val
|
||||
return
|
||||
# その key が存在しなければ、キーと値のペアを末尾に追加
|
||||
# key が存在しない場合、キーと値のペアを末尾に追加する
|
||||
pair = Pair(key, val)
|
||||
bucket.append(pair)
|
||||
self.size += 1
|
||||
@@ -62,7 +62,7 @@ class HashMapChaining:
|
||||
"""削除操作"""
|
||||
index = self.hash_func(key)
|
||||
bucket = self.buckets[index]
|
||||
# バケットを走査してキーと値のペアを削除
|
||||
# バケットを走査し、キーと値のペアを削除する
|
||||
for pair in bucket:
|
||||
if pair.key == key:
|
||||
bucket.remove(pair)
|
||||
@@ -70,20 +70,20 @@ class HashMapChaining:
|
||||
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:
|
||||
@@ -91,28 +91,28 @@ class HashMapChaining:
|
||||
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, ...]")
|
||||
# ハッシュテーブルにキーと値のペア (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 を取得する
|
||||
# 取得操作
|
||||
# ハッシュテーブルにキー key を渡し、値 value を取得する
|
||||
name = hashmap.get(13276)
|
||||
print("\n学籍番号 13276 を入力すると、氏名は " + name)
|
||||
print("\n学生番号 13276 に対応する氏名は " + name)
|
||||
|
||||
# 削除操作
|
||||
# ハッシュテーブルからキーと値の組 (key, value) を削除する
|
||||
# ハッシュテーブルからキーと値のペア (key, value) を削除する
|
||||
hashmap.remove(12836)
|
||||
print("\n12836 を削除した後、ハッシュテーブルは\n[Key1 -> Value1, Key2 -> Value2, ...]")
|
||||
print("\n12836 を削除した後のハッシュテーブル\n[Key1 -> Value1, Key2 -> Value2, ...]")
|
||||
hashmap.print()
|
||||
|
||||
@@ -12,16 +12,16 @@ 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.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") # 削除済みマーク
|
||||
self.TOMBSTONE = Pair(-1, "-1") # 削除マーカー
|
||||
|
||||
def hash_func(self, key: int) -> int:
|
||||
"""ハッシュ関数"""
|
||||
@@ -32,76 +32,76 @@ class HashMapOpenAddressing:
|
||||
return self.size / self.capacity
|
||||
|
||||
def find_bucket(self, key: int) -> int:
|
||||
"""key に対応するバケットインデックスを探す"""
|
||||
"""key に対応するバケットのインデックスを探索する"""
|
||||
index = self.hash_func(key)
|
||||
first_tombstone = -1
|
||||
# 線形プロービングを行い、空バケットに達したら終了
|
||||
# 線形探索法で探索し、空のバケットに到達したら終了する
|
||||
while self.buckets[index] is not None:
|
||||
# key が見つかったら、対応するバケットのインデックスを返す
|
||||
# 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 first_tombstone # 移動後のバケットのインデックスを返す
|
||||
return index # バケットのインデックスを返す
|
||||
# 最初に見つかった削除マークを記録
|
||||
# 最初に見つかった削除マーカーを記録する
|
||||
if first_tombstone == -1 and self.buckets[index] is self.TOMBSTONE:
|
||||
first_tombstone = index
|
||||
# バケットのインデックスを計算し、末尾を越えたら先頭に戻る
|
||||
index = (index + 1) % self.capacity
|
||||
# key が存在しない場合は追加位置のインデックスを返す
|
||||
# key が存在しない場合、追加位置のインデックスを返す
|
||||
return index if first_tombstone == -1 else first_tombstone
|
||||
|
||||
def get(self, key: int) -> str:
|
||||
"""検索操作"""
|
||||
# key に対応するバケットインデックスを探す
|
||||
"""取得操作"""
|
||||
# key に対応するバケットのインデックスを探索する
|
||||
index = self.find_bucket(key)
|
||||
# キーと値の組が見つかったら、対応する val を返す
|
||||
# キーと値のペアが見つかった場合、対応する val を返す
|
||||
if self.buckets[index] not in [None, self.TOMBSTONE]:
|
||||
return self.buckets[index].val
|
||||
# キーと値のペアが存在しない場合は `None` を返す
|
||||
# キーと値のペアが存在しない場合、None を返す
|
||||
return None
|
||||
|
||||
def put(self, key: int, val: str):
|
||||
"""追加操作"""
|
||||
# 負荷率がしきい値を超えたら、リサイズを実行
|
||||
# 負荷率がしきい値を超えた場合、容量を拡張する
|
||||
if self.load_factor() > self.load_thres:
|
||||
self.extend()
|
||||
# key に対応するバケットインデックスを探す
|
||||
# key に対応するバケットのインデックスを探索する
|
||||
index = self.find_bucket(key)
|
||||
# キーと値の組が見つかったら、val を上書きして返す
|
||||
# キーと値のペアが見つかった場合、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 に対応するバケットインデックスを探す
|
||||
# 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")
|
||||
@@ -111,28 +111,28 @@ class HashMapOpenAddressing:
|
||||
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追加完了後、ハッシュテーブルは\nKey -> Value")
|
||||
# ハッシュテーブルにキーと値のペア (key, val) を追加する
|
||||
hashmap.put(12836, "小哈")
|
||||
hashmap.put(15937, "小啰")
|
||||
hashmap.put(16750, "小算")
|
||||
hashmap.put(13276, "小法")
|
||||
hashmap.put(10583, "小鸭")
|
||||
print("\n追加後のハッシュテーブル\nKey -> Value")
|
||||
hashmap.print()
|
||||
|
||||
# 検索操作
|
||||
# ハッシュテーブルにキー key を入力し、値 val を得る
|
||||
# 取得操作
|
||||
# ハッシュテーブルにキー key を渡し、値 val を取得する
|
||||
name = hashmap.get(13276)
|
||||
print("\n学籍番号 13276 を入力すると、氏名は " + name)
|
||||
print("\n学生番号 13276 に対応する氏名は " + name)
|
||||
|
||||
# 削除操作
|
||||
# ハッシュテーブルからキーと値の組 (key, val) を削除する
|
||||
# ハッシュテーブルからキーと値のペア (key, val) を削除する
|
||||
hashmap.remove(16750)
|
||||
print("\n16750 を削除した後、ハッシュテーブルは\nKey -> Value")
|
||||
print("\n16750 を削除した後のハッシュテーブル\nKey -> Value")
|
||||
hashmap.print()
|
||||
|
||||
@@ -24,7 +24,7 @@ def mul_hash(key: str) -> int:
|
||||
|
||||
|
||||
def xor_hash(key: str) -> int:
|
||||
"""XOR ハッシュ"""
|
||||
"""排他的論理和ハッシュ"""
|
||||
hash = 0
|
||||
modulus = 1000000007
|
||||
for c in key:
|
||||
@@ -41,18 +41,18 @@ def rot_hash(key: str) -> int:
|
||||
return hash % modulus
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
key = "Hello アルゴリズム"
|
||||
key = "Hello 算法"
|
||||
|
||||
hash = add_hash(key)
|
||||
print(f"加算ハッシュ値は {hash}")
|
||||
print(f"加算ハッシュの値は {hash}")
|
||||
|
||||
hash = mul_hash(key)
|
||||
print(f"乗算ハッシュ値は {hash}")
|
||||
print(f"乗算ハッシュの値は {hash}")
|
||||
|
||||
hash = xor_hash(key)
|
||||
print(f"XOR ハッシュ値は {hash}")
|
||||
print(f"排他的論理和ハッシュの値は {hash}")
|
||||
|
||||
hash = rot_hash(key)
|
||||
print(f"回転ハッシュ値は {hash}")
|
||||
print(f"回転ハッシュの値は {hash}")
|
||||
|
||||
@@ -14,54 +14,54 @@ import heapq
|
||||
|
||||
|
||||
def test_push(heap: list, val: int, flag: int = 1):
|
||||
heapq.heappush(heap, flag * val) # 要素をヒープに追加
|
||||
print(f"\n要素 {val} をヒープに追加した後")
|
||||
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} を取り出した後")
|
||||
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以下のテストケースは最大ヒープ")
|
||||
# Python の heapq モジュールはデフォルトで最小ヒープを実装している
|
||||
# 要素を負にしてからヒープに入れると大小関係を反転でき、最大ヒープを実現できる
|
||||
# この例では、flag = 1 が最小ヒープ、flag = -1 が最大ヒープに対応する
|
||||
print("\n以下のテストケースは最大ヒープを対象とする")
|
||||
# Python の heapq モジュールは既定で最小ヒープを実装する
|
||||
# 「要素の符号を反転」してからヒープに挿入すれば、大小関係が逆転し、最大ヒープを実装できる
|
||||
# この例では 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}")
|
||||
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}")
|
||||
print(f"\nヒープの要素数は {size}")
|
||||
|
||||
# ヒープが空かどうかを判定
|
||||
# ヒープが空か判定
|
||||
is_empty: bool = not max_heap
|
||||
print(f"\nヒープが空かどうかは {is_empty}")
|
||||
print(f"\nヒープは空か {is_empty}")
|
||||
|
||||
# リストを入力してヒープを構築する
|
||||
# 時間計算量は O(n) であり、O(nlogn) ではない
|
||||
|
||||
@@ -15,123 +15,123 @@ class MaxHeap:
|
||||
"""最大ヒープ"""
|
||||
|
||||
def __init__(self, nums: list[int]):
|
||||
"""コンストラクタ。入力リストに基づいてヒープを構築する"""
|
||||
# リスト要素をそのままヒープに追加
|
||||
"""コンストラクタ:入力リストからヒープを構築する"""
|
||||
# リストの要素をそのままヒープに追加する
|
||||
self.max_heap = nums
|
||||
# 葉ノード以外のすべてのノードをヒープ化
|
||||
# 葉ノード以外の全ノードをヒープ化する
|
||||
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 から始めて、下から上へヒープ化"""
|
||||
"""ノード i から上向きにヒープ化する"""
|
||||
while True:
|
||||
# ノード i の親ノードを取得
|
||||
# ノード i の親ノードを取得する
|
||||
p = self.parent(i)
|
||||
# 「根ノードを越えた」または「ノードの修復が不要」になったらヒープ化を終了
|
||||
# 「根ノードを越えた」または「ノードの修復が不要」の場合、ヒープ化を終了する
|
||||
if p < 0 or self.max_heap[i] <= self.max_heap[p]:
|
||||
break
|
||||
# 2 つのノードを交換
|
||||
# 2 つのノードを交換する
|
||||
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 から始めて、上から下へヒープ化"""
|
||||
"""ノード i から下向きにヒープ化する"""
|
||||
while True:
|
||||
# ノード i, l, r のうち値が最大のノードを ma とする
|
||||
# ノード 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 が範囲外なら、ヒープ化は不要なので抜ける
|
||||
# ノード i が最大か添字 l, r が範囲外なら、ヒープ化は不要なので終了
|
||||
if ma == i:
|
||||
break
|
||||
# 2 つのノードを交換
|
||||
# 2 つのノードを交換する
|
||||
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}")
|
||||
print(f"\nヒープの先頭要素は {peek}")
|
||||
|
||||
# 要素をヒープに追加
|
||||
# ヒープに要素を挿入する
|
||||
val = 7
|
||||
max_heap.push(val)
|
||||
print(f"\n要素 {val} をヒープに追加した後")
|
||||
print(f"\n要素 {val} をヒープに挿入した後")
|
||||
max_heap.print()
|
||||
|
||||
# ヒープ頂点の要素を取り出す
|
||||
# ヒープの先頭要素を取り出す
|
||||
peek = max_heap.pop()
|
||||
print(f"\nヒープ先頭要素 {peek} を取り出した後")
|
||||
print(f"\nヒープの先頭要素 {peek} を取り出した後")
|
||||
max_heap.print()
|
||||
|
||||
# ヒープのサイズを取得
|
||||
# ヒープのサイズを取得する
|
||||
size = max_heap.size()
|
||||
print(f"\nヒープ要素数は {size}")
|
||||
print(f"\nヒープの要素数は {size}")
|
||||
|
||||
# ヒープが空かどうかを判定
|
||||
# ヒープが空か判定
|
||||
is_empty = max_heap.is_empty()
|
||||
print(f"\nヒープが空かどうかは {is_empty}")
|
||||
print(f"\nヒープは空か {is_empty}")
|
||||
|
||||
@@ -14,26 +14,26 @@ import heapq
|
||||
|
||||
|
||||
def top_k_heap(nums: list[int], k: int) -> list[int]:
|
||||
"""ヒープに基づいて配列中の最大の k 個の要素を探す"""
|
||||
# 最小ヒープを初期化
|
||||
"""ヒープを使って配列内の大きいほうから k 個の要素を求める"""
|
||||
# 最小ヒープを初期化する
|
||||
heap = []
|
||||
# 配列の先頭 k 個の要素をヒープに追加
|
||||
# 配列の先頭 k 個の要素をヒープに挿入する
|
||||
for i in range(k):
|
||||
heapq.heappush(heap, nums[i])
|
||||
# k+1 番目の要素から開始し、ヒープ長を k に保つ
|
||||
# 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(f"大きいほうから {k} 個の要素は")
|
||||
print_heap(res)
|
||||
|
||||
@@ -6,47 +6,47 @@ Author: timi (xisunyy@163.com)
|
||||
|
||||
|
||||
def binary_search(nums: list[int], target: int) -> int:
|
||||
"""二分探索(両閉区間)"""
|
||||
# 両閉区間 [0, n-1] を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素を指す
|
||||
"""二分探索(閉区間)"""
|
||||
# 閉区間 [0, n-1] を初期化する。i, j は配列の先頭要素と末尾要素をそれぞれ指す
|
||||
i, j = 0, len(nums) - 1
|
||||
# ループし、探索区間が空になったら終了する(i > j で空)
|
||||
# 探索区間が空になるまで繰り返す(i > j のとき空になる)
|
||||
while i <= j:
|
||||
# 理論上、Python の数値は無限に大きくできるため(メモリ容量に依存)、大きな数のオーバーフローを考慮する必要はない
|
||||
m = (i + j) // 2 # 中点インデックス m を計算
|
||||
# 理論上、Python の数値は無限に大きくできる(メモリ容量による)ため、大きな数のオーバーフローを考慮する必要がない
|
||||
m = (i + j) // 2 # 中央のインデックス m を計算する
|
||||
if nums[m] < target:
|
||||
i = m + 1 # この場合、target は区間 [m+1, j] にある
|
||||
i = m + 1 # この場合、target は区間 [m+1, j] に存在する
|
||||
elif nums[m] > target:
|
||||
j = m - 1 # この場合、target は区間 [i, m-1] にある
|
||||
j = m - 1 # この場合、target は区間 [i, m-1] に存在する
|
||||
else:
|
||||
return m # 目標要素が見つかったらそのインデックスを返す
|
||||
return -1 # 目標要素が見つからなければ -1 を返す
|
||||
return m # 目的の要素が見つかったので、そのインデックスを返す
|
||||
return -1 # 目的の要素が見つからなかったので、-1 を返す
|
||||
|
||||
|
||||
def binary_search_lcro(nums: list[int], target: int) -> int:
|
||||
"""二分探索(左閉右開区間)"""
|
||||
# 左閉右開区間 [0, n) を初期化する。つまり i, j はそれぞれ配列の先頭要素と末尾要素+1を指す
|
||||
# 左閉右開区間 [0, n) を初期化する。i, j は配列の先頭要素と末尾要素の次をそれぞれ指す
|
||||
i, j = 0, len(nums)
|
||||
# ループし、探索区間が空になったら終了する(i = j で空)
|
||||
# 探索区間が空になるまで繰り返す(i = j のとき空になる)
|
||||
while i < j:
|
||||
m = (i + j) // 2 # 中点インデックス m を計算
|
||||
m = (i + j) // 2 # 中央のインデックス m を計算する
|
||||
if nums[m] < target:
|
||||
i = m + 1 # この場合、target は区間 [m+1, j) にある
|
||||
i = m + 1 # この場合、target は区間 [m+1, j) に存在する
|
||||
elif nums[m] > target:
|
||||
j = m # この場合、target は区間 [i, m) にある
|
||||
j = m # この場合、target は区間 [i, m) に存在する
|
||||
else:
|
||||
return m # 目標要素が見つかったらそのインデックスを返す
|
||||
return -1 # 目標要素が見つからなければ -1 を返す
|
||||
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)
|
||||
print("目的の要素 6 のインデックス = ", index)
|
||||
|
||||
# 二分探索(左閉右開区間)
|
||||
index = binary_search_lcro(nums, target)
|
||||
print("対象要素 6 のインデックス = ", index)
|
||||
print("目的の要素 6 のインデックス = ", index)
|
||||
|
||||
@@ -12,38 +12,38 @@ from binary_search_insertion import binary_search_insertion
|
||||
|
||||
|
||||
def binary_search_left_edge(nums: list[int], target: int) -> int:
|
||||
"""最も左の target を二分探索"""
|
||||
# target の挿入位置を探すのと等価
|
||||
"""二分探索で最も左にある target を探す"""
|
||||
# target の挿入位置を探すことに等しい
|
||||
i = binary_search_insertion(nums, target)
|
||||
# target が見つからなければ、-1 を返す
|
||||
# target が見つからなかったので、-1 を返す
|
||||
if i == len(nums) or nums[i] != target:
|
||||
return -1
|
||||
# target が見つかったら、インデックス i を返す
|
||||
# target が見つかったので、インデックス i を返す
|
||||
return i
|
||||
|
||||
|
||||
def binary_search_right_edge(nums: list[int], target: int) -> int:
|
||||
"""最も右の target を二分探索"""
|
||||
# 最左の target + 1 を探す問題に変換する
|
||||
"""二分探索で最も右にある target を探す"""
|
||||
# 最も左にある target + 1 を探す問題に変換する
|
||||
i = binary_search_insertion(nums, target + 1)
|
||||
# j は最も右の target を指し、i は target より大きい最初の要素を指す
|
||||
# j は最も右にある target を指し、i は target より大きい最初の要素を指す
|
||||
j = i - 1
|
||||
# target が見つからなければ、-1 を返す
|
||||
# target が見つからなかったので、-1 を返す
|
||||
if j == -1 or nums[j] != target:
|
||||
return -1
|
||||
# target が見つかったら、インデックス j を返す
|
||||
# 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}")
|
||||
print(f"最も左にある要素 {target} のインデックスは {index}")
|
||||
index = binary_search_right_edge(nums, target)
|
||||
print(f"右端の要素 {target} のインデックスは {index}")
|
||||
print(f"最も右にある要素 {target} のインデックスは {index}")
|
||||
|
||||
@@ -6,41 +6,41 @@ 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] を初期化
|
||||
"""二分探索による挿入位置の探索(重複要素がない場合)"""
|
||||
i, j = 0, len(nums) - 1 # 閉区間 [0, n-1] を初期化する
|
||||
while i <= j:
|
||||
m = (i + j) // 2 # 中点インデックス m を計算
|
||||
m = (i + j) // 2 # 中央のインデックス m を計算する
|
||||
if nums[m] < target:
|
||||
i = m + 1 # target は区間 [m+1, j] にある
|
||||
i = m + 1 # target は区間 [m+1, j] に存在する
|
||||
elif nums[m] > target:
|
||||
j = m - 1 # target は区間 [i, m-1] にある
|
||||
j = m - 1 # target は区間 [i, m-1] に存在する
|
||||
else:
|
||||
return m # target が見つかったら、挿入位置 m を返す
|
||||
# target が見つからなければ、挿入位置 i を返す
|
||||
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] を初期化
|
||||
"""二分探索による挿入位置の探索(重複要素がある場合)"""
|
||||
i, j = 0, len(nums) - 1 # 閉区間 [0, n-1] を初期化する
|
||||
while i <= j:
|
||||
m = (i + j) // 2 # 中点インデックス m を計算
|
||||
m = (i + j) // 2 # 中央のインデックス m を計算する
|
||||
if nums[m] < target:
|
||||
i = m + 1 # target は区間 [m+1, j] にある
|
||||
i = m + 1 # target は区間 [m+1, j] に存在する
|
||||
elif nums[m] > target:
|
||||
j = m - 1 # target は区間 [i, m-1] にある
|
||||
j = m - 1 # target は区間 [i, m-1] に存在する
|
||||
else:
|
||||
j = m - 1 # target より小さい最も右の要素は区間 [i, m-1] にある
|
||||
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}")
|
||||
@@ -48,7 +48,7 @@ if __name__ == "__main__":
|
||||
# 重複要素を含む配列
|
||||
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}")
|
||||
|
||||
@@ -13,8 +13,8 @@ from modules import ListNode, list_to_linked_list
|
||||
|
||||
def hashing_search_array(hmap: dict[int, int], target: int) -> int:
|
||||
"""ハッシュ探索(配列)"""
|
||||
# ハッシュテーブルの key: 目標要素、value: インデックス
|
||||
# ハッシュテーブルにこの key がなければ -1 を返す
|
||||
# ハッシュテーブルの key:目的の要素、value:インデックス
|
||||
# ハッシュテーブルにこの key が存在しない場合、-1 を返す
|
||||
return hmap.get(target, -1)
|
||||
|
||||
|
||||
@@ -22,30 +22,30 @@ def hashing_search_linkedlist(
|
||||
hmap: dict[int, ListNode], target: int
|
||||
) -> ListNode | None:
|
||||
"""ハッシュ探索(連結リスト)"""
|
||||
# ハッシュテーブルの key: 対象要素、value: ノードオブジェクト
|
||||
# ハッシュテーブルにこの key がなければ 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: インデックス
|
||||
map0[nums[i]] = i # key:要素、value:インデックス
|
||||
index: int = hashing_search_array(map0, target)
|
||||
print("対象要素 3 のインデックス =", index)
|
||||
print("目的の要素 3 のインデックス =", index)
|
||||
|
||||
# ハッシュ探索(連結リスト)
|
||||
head: ListNode = list_to_linked_list(nums)
|
||||
# ハッシュテーブルを初期化
|
||||
# ハッシュテーブルを初期化する
|
||||
map1 = dict[int, ListNode]()
|
||||
while head:
|
||||
map1[head.val] = head # key: ノード値、value: ノード
|
||||
map1[head.val] = head # key:ノードの値、value:ノード
|
||||
head = head.next
|
||||
node: ListNode = hashing_search_linkedlist(map1, target)
|
||||
print("対象ノード値 3 に対応するノードオブジェクトは", node)
|
||||
print("目的のノードの値 3 に対応するノードオブジェクトは", node)
|
||||
|
||||
@@ -13,33 +13,33 @@ 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: # 目標要素が見つかったらそのインデックスを返す
|
||||
if nums[i] == target: # 目的の要素が見つかったので、そのインデックスを返す
|
||||
return i
|
||||
return -1 # 目標要素が見つからなければ -1 を返す
|
||||
return -1 # 目的の要素が見つからなかったので、-1 を返す
|
||||
|
||||
|
||||
def linear_search_linkedlist(head: ListNode, target: int) -> ListNode | None:
|
||||
"""線形探索(連結リスト)"""
|
||||
# 連結リストを走査
|
||||
# 連結リストを走査する
|
||||
while head:
|
||||
if head.val == target: # 対象ノードが見つかったら、それを返す
|
||||
if head.val == target: # 目的のノードが見つかったので、それを返す
|
||||
return head
|
||||
head = head.next
|
||||
return None # 対象ノードが見つからない場合は None を返す
|
||||
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)
|
||||
print("目的の要素 3 のインデックス =", index)
|
||||
|
||||
# 連結リストで線形探索を行う
|
||||
head: ListNode = list_to_linked_list(nums)
|
||||
node: ListNode | None = linear_search_linkedlist(head, target)
|
||||
print("対象ノード値 3 に対応するノードオブジェクトは", node)
|
||||
print("目的のノードの値 3 に対応するノードオブジェクトは", node)
|
||||
|
||||
@@ -7,7 +7,7 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
def two_sum_brute_force(nums: list[int], target: int) -> list[int]:
|
||||
"""方法 1:総当たり列挙"""
|
||||
# 2重ループのため、時間計算量は O(n^2)
|
||||
# 二重ループのため、時間計算量は O(n^2)
|
||||
for i in range(len(nums) - 1):
|
||||
for j in range(i + 1, len(nums)):
|
||||
if nums[i] + nums[j] == target:
|
||||
@@ -17,9 +17,9 @@ def two_sum_brute_force(nums: list[int], target: int) -> list[int]:
|
||||
|
||||
def two_sum_hash_table(nums: list[int], target: int) -> list[int]:
|
||||
"""方法 2:補助ハッシュテーブル"""
|
||||
# 補助ハッシュテーブルを使用し、空間計算量は O(n)
|
||||
# 補助ハッシュテーブルの空間計算量は O(n)
|
||||
dic = {}
|
||||
# 単一ループで、時間計算量は O(n)
|
||||
# 一重ループのため、時間計算量は O(n)
|
||||
for i in range(len(nums)):
|
||||
if target - nums[i] in dic:
|
||||
return [dic[target - nums[i]], i]
|
||||
@@ -27,16 +27,16 @@ def two_sum_hash_table(nums: list[int], target: int) -> list[int]:
|
||||
return []
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# ======= Test Case =======
|
||||
# ======= テストケース =======
|
||||
nums = [2, 7, 11, 15]
|
||||
target = 13
|
||||
|
||||
# ====== Driver Code ======
|
||||
# ====== 実行用コード ======
|
||||
# 方法 1
|
||||
res: list[int] = two_sum_brute_force(nums, target)
|
||||
print("方法1 res =", res)
|
||||
print("方法 1 res =", res)
|
||||
# 方法 2
|
||||
res: list[int] = two_sum_hash_table(nums, target)
|
||||
print("方法2 res =", res)
|
||||
print("方法 2 res =", res)
|
||||
|
||||
@@ -10,30 +10,30 @@ def bubble_sort(nums: list[int]):
|
||||
n = len(nums)
|
||||
# 外側のループ:未ソート区間は [0, i]
|
||||
for i in range(n - 1, 0, -1):
|
||||
# 内側のループ:未ソート区間 [0, i] の最大要素をその区間の最右端へ交換
|
||||
# 内側のループ:未ソート区間 [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], 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] の最大要素をその区間の最右端へ交換
|
||||
# 内側のループ:未ソート区間 [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], nums[j + 1] = nums[j + 1], nums[j]
|
||||
flag = True # 交換する要素を記録
|
||||
flag = True # 要素を交換したことを記録する
|
||||
if not flag:
|
||||
break # このバブル処理で要素交換が一度もなければそのまま終了
|
||||
break # 今回の「バブル」で要素を一度も交換しなかった場合、直ちに終了する
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
nums = [4, 1, 3, 1, 5, 2]
|
||||
bubble_sort(nums)
|
||||
|
||||
@@ -7,20 +7,20 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
def bucket_sort(nums: list[float]):
|
||||
"""バケットソート"""
|
||||
# k = n/2 個のバケットを初期化し、各バケットに 2 要素ずつ割り当てる想定とする
|
||||
# k = n/2 個のバケットを初期化し、各バケットに平均 2 個の要素を割り当てることを想定する
|
||||
k = len(nums) // 2
|
||||
buckets = [[] for _ in range(k)]
|
||||
# 1. 配列要素を各バケットに振り分ける
|
||||
# 1. 配列の要素を各バケットに振り分ける
|
||||
for num in nums:
|
||||
# 入力データの範囲は [0, 1) であり、num * k を用いてインデックス範囲 [0, k-1] に写像する
|
||||
# 入力データの範囲は [0, 1) なので、num * k によりインデックス範囲 [0, k-1] に対応付ける
|
||||
i = int(num * k)
|
||||
# num をバケット i に追加
|
||||
# num をバケット i に追加する
|
||||
buckets[i].append(num)
|
||||
# 2. 各バケットをソートする
|
||||
for bucket in buckets:
|
||||
# 組み込みのソート関数を使う。他のソートアルゴリズムに置き換えてもよい
|
||||
bucket.sort()
|
||||
# 3. バケットを走査して結果を結合
|
||||
# 3. バケットを走査して結果を結合する
|
||||
i = 0
|
||||
for bucket in buckets:
|
||||
for num in bucket:
|
||||
@@ -29,7 +29,7 @@ def bucket_sort(nums: list[float]):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 入力データは範囲 [0, 1) の浮動小数点数とする
|
||||
# 入力データは [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)
|
||||
|
||||
@@ -6,8 +6,8 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def counting_sort_naive(nums: list[int]):
|
||||
"""計数ソート"""
|
||||
# 簡易版。オブジェクトのソートには使えない
|
||||
"""カウントソート"""
|
||||
# 簡易実装。オブジェクトのソートには使えない
|
||||
# 1. 配列の最大要素 m を求める
|
||||
m = max(nums)
|
||||
# 2. 各数値の出現回数を数える
|
||||
@@ -15,7 +15,7 @@ def counting_sort_naive(nums: list[int]):
|
||||
counter = [0] * (m + 1)
|
||||
for num in nums:
|
||||
counter[num] += 1
|
||||
# 3. counter を走査し、各要素を元の配列 nums に書き戻す
|
||||
# 3. counter を走査し、各要素を元の配列 nums に入れる
|
||||
i = 0
|
||||
for num in range(m + 1):
|
||||
for _ in range(counter[num]):
|
||||
@@ -24,8 +24,8 @@ def counting_sort_naive(nums: list[int]):
|
||||
|
||||
|
||||
def counting_sort(nums: list[int]):
|
||||
"""計数ソート"""
|
||||
# 完全版。オブジェクトをソートでき、かつ安定ソートである
|
||||
"""カウントソート"""
|
||||
# 完全な実装。オブジェクトをソートでき、安定ソートである
|
||||
# 1. 配列の最大要素 m を求める
|
||||
m = max(nums)
|
||||
# 2. 各数値の出現回数を数える
|
||||
@@ -33,30 +33,30 @@ def counting_sort(nums: list[int]):
|
||||
counter = [0] * (m + 1)
|
||||
for num in nums:
|
||||
counter[num] += 1
|
||||
# 3. counter の累積和を求めて、「出現回数」を「末尾インデックス」に変換する
|
||||
# つまり counter[num]-1 は、num が res に最後に現れるインデックス
|
||||
# 3. counter の累積和を求め、「出現回数」を「末尾のインデックス」に変換する
|
||||
# つまり、counter[num]-1 は res 内で num が最後に現れるインデックスである
|
||||
for i in range(m):
|
||||
counter[i + 1] += counter[i]
|
||||
# 4. nums を逆順に走査し、各要素を結果配列 res に格納する
|
||||
# 結果を記録するための配列 res を初期化
|
||||
# 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 を配置するインデックスを得る
|
||||
res[counter[num] - 1] = num # 対応するインデックスに num を配置する
|
||||
counter[num] -= 1 # 累積和を 1 減らし、次に num を配置するインデックスを求める
|
||||
# 結果配列 res で元の配列 nums を上書きする
|
||||
for i in range(n):
|
||||
nums[i] = res[i]
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]
|
||||
|
||||
counting_sort_naive(nums)
|
||||
print(f"計数ソート(オブジェクトはソート不可)完了後 nums = {nums}")
|
||||
print(f"カウントソート(オブジェクトはソート不可)の完了後 nums = {nums}")
|
||||
|
||||
nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]
|
||||
counting_sort(nums1)
|
||||
print(f"計数ソート完了後 nums1 = {nums1}")
|
||||
print(f"カウントソート完了後 nums1 = {nums1}")
|
||||
|
||||
@@ -6,9 +6,9 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def sift_down(nums: list[int], n: int, i: int):
|
||||
"""ヒープの長さは n。ノード i から下方向にヒープ化"""
|
||||
"""長さ n のヒープを、ノード i から下向きにヒープ化する"""
|
||||
while True:
|
||||
# ノード i, l, r のうち値が最大のノードを ma とする
|
||||
# ノード i, l, r のうち値が最大のノードを求め、ma とする
|
||||
l = 2 * i + 1
|
||||
r = 2 * i + 2
|
||||
ma = i
|
||||
@@ -16,29 +16,29 @@ def sift_down(nums: list[int], n: int, i: int):
|
||||
ma = l
|
||||
if r < n and nums[r] > nums[ma]:
|
||||
ma = r
|
||||
# ノード i が最大、またはインデックス l, r が範囲外なら、ヒープ化は不要なので抜ける
|
||||
# ノード i が最大か添字 l, r が範囲外なら、ヒープ化は不要なので終了
|
||||
if ma == i:
|
||||
break
|
||||
# 2 つのノードを交換
|
||||
# 2 つのノードを交換する
|
||||
nums[i], nums[ma] = nums[ma], nums[i]
|
||||
# ループで上から下へヒープ化
|
||||
# 下向きのヒープ化を繰り返す
|
||||
i = ma
|
||||
|
||||
|
||||
def heap_sort(nums: list[int]):
|
||||
"""ヒープソート"""
|
||||
# ヒープ構築:葉ノード以外のすべてのノードをヒープ化する
|
||||
# ヒープの構築:葉ノード以外の全ノードをヒープ化する
|
||||
for i in range(len(nums) // 2 - 1, -1, -1):
|
||||
sift_down(nums, len(nums), i)
|
||||
# ヒープから最大要素を取り出し、n-1 回繰り返す
|
||||
# ヒープから最大要素を取り出す処理を 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)
|
||||
|
||||
@@ -7,18 +7,18 @@ Author: timi (xisunyy@163.com)
|
||||
|
||||
def insertion_sort(nums: list[int]):
|
||||
"""挿入ソート"""
|
||||
# 外側ループ:整列済み区間は [0, i-1]
|
||||
# 外側のループ:ソート済み区間は [0, i-1]
|
||||
for i in range(1, len(nums)):
|
||||
base = nums[i]
|
||||
j = i - 1
|
||||
# 内側ループ: base をソート済み区間 [0, i-1] の正しい位置に挿入する
|
||||
# 内側のループ:ソート済み区間 [0, i-1] の正しい位置に base を挿入する
|
||||
while j >= 0 and nums[j] > base:
|
||||
nums[j + 1] = nums[j] # nums[j] を 1 つ右へ移動する
|
||||
nums[j + 1] = nums[j] # nums[j] を右に 1 つずらす
|
||||
j -= 1
|
||||
nums[j + 1] = base # base を正しい位置に配置する
|
||||
nums[j + 1] = base # 正しい位置に base を代入する
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
nums = [4, 1, 3, 1, 5, 2]
|
||||
insertion_sort(nums)
|
||||
|
||||
@@ -6,13 +6,13 @@ 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 を作成する
|
||||
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]
|
||||
@@ -21,7 +21,7 @@ def merge(nums: list[int], left: int, mid: int, right: int):
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
# 左右の部分配列の残り要素を一時配列にコピーする
|
||||
# 左部分配列と右部分配列の残りの要素を一時配列にコピーする
|
||||
while i <= mid:
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
@@ -30,7 +30,7 @@ def merge(nums: list[int], left: int, mid: int, right: int):
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
# 一時配列 tmp の要素を元の配列 nums の対応区間にコピーする
|
||||
# 一時配列 tmp の要素を元の配列 nums の対応する区間にコピーし戻す
|
||||
for k in range(0, len(tmp)):
|
||||
nums[left + k] = tmp[k]
|
||||
|
||||
@@ -39,16 +39,16 @@ 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) # 右部分配列を再帰処理
|
||||
# マージフェーズ
|
||||
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)
|
||||
|
||||
@@ -9,121 +9,121 @@ class QuickSort:
|
||||
"""クイックソートクラス"""
|
||||
|
||||
def partition(self, nums: list[int], left: int, right: int) -> int:
|
||||
"""番兵分割"""
|
||||
# nums[left] を基準値とする
|
||||
"""番兵を用いた分割"""
|
||||
# nums[left] をピボットとする
|
||||
i, j = left, right
|
||||
while i < j:
|
||||
while i < j and nums[j] >= nums[left]:
|
||||
j -= 1 # 右から左へ基準値未満の最初の要素を探す
|
||||
j -= 1 # 右から左へ、ピボットより小さい最初の要素を探す
|
||||
while i < j and nums[i] <= nums[left]:
|
||||
i += 1 # 左から右へ基準値より大きい最初の要素を探す
|
||||
i += 1 # 左から右へ、ピボットより大きい最初の要素を探す
|
||||
# 要素の交換
|
||||
nums[i], nums[j] = nums[j], nums[i]
|
||||
# 基準値を 2 つの部分配列の境界へ交換する
|
||||
# ピボットを 2 つの部分配列の境界に移す
|
||||
nums[i], nums[left] = nums[left], nums[i]
|
||||
return i # 基準値のインデックスを返す
|
||||
return i # ピボットのインデックスを返す
|
||||
|
||||
def quick_sort(self, nums: list[int], left: int, right: int):
|
||||
"""クイックソート"""
|
||||
# 部分配列の長さが 1 なら再帰を終了する
|
||||
# 部分配列の長さが 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:
|
||||
"""3つの候補要素の中央値を選ぶ"""
|
||||
"""候補となる 3 要素の中央値を選ぶ"""
|
||||
l, m, r = nums[left], nums[mid], nums[right]
|
||||
if (l <= m <= r) or (r <= m <= l):
|
||||
return mid # m は l と r の間
|
||||
return mid # m が l と r の間にある
|
||||
if (m <= l <= r) or (r <= l <= m):
|
||||
return left # l は m と r の間
|
||||
return left # l が m と r の間にある
|
||||
return right
|
||||
|
||||
def partition(self, nums: list[int], left: int, right: int) -> int:
|
||||
"""番兵による分割処理(3 点中央値)"""
|
||||
# nums[left] を基準値とする
|
||||
"""番兵を用いた分割(3 値の中央値を選択)"""
|
||||
# nums[left] をピボットとする
|
||||
med = self.median_three(nums, left, (left + right) // 2, right)
|
||||
# 中央値を配列の最左端に交換する
|
||||
# 中央値を配列の左端に移す
|
||||
nums[left], nums[med] = nums[med], nums[left]
|
||||
# nums[left] を基準値とする
|
||||
# nums[left] をピボットとする
|
||||
i, j = left, right
|
||||
while i < j:
|
||||
while i < j and nums[j] >= nums[left]:
|
||||
j -= 1 # 右から左へ基準値未満の最初の要素を探す
|
||||
j -= 1 # 右から左へ、ピボットより小さい最初の要素を探す
|
||||
while i < j and nums[i] <= nums[left]:
|
||||
i += 1 # 左から右へ基準値より大きい最初の要素を探す
|
||||
i += 1 # 左から右へ、ピボットより大きい最初の要素を探す
|
||||
# 要素の交換
|
||||
nums[i], nums[j] = nums[j], nums[i]
|
||||
# 基準値を 2 つの部分配列の境界へ交換する
|
||||
# ピボットを 2 つの部分配列の境界に移す
|
||||
nums[i], nums[left] = nums[left], nums[i]
|
||||
return i # 基準値のインデックスを返す
|
||||
return i # ピボットのインデックスを返す
|
||||
|
||||
def quick_sort(self, nums: list[int], left: int, right: int):
|
||||
"""クイックソート"""
|
||||
# 部分配列の長さが 1 なら再帰を終了する
|
||||
# 部分配列の長さが 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] を基準値とする
|
||||
"""番兵を用いた分割"""
|
||||
# nums[left] をピボットとする
|
||||
i, j = left, right
|
||||
while i < j:
|
||||
while i < j and nums[j] >= nums[left]:
|
||||
j -= 1 # 右から左へ基準値未満の最初の要素を探す
|
||||
j -= 1 # 右から左へ、ピボットより小さい最初の要素を探す
|
||||
while i < j and nums[i] <= nums[left]:
|
||||
i += 1 # 左から右へ基準値より大きい最初の要素を探す
|
||||
i += 1 # 左から右へ、ピボットより大きい最初の要素を探す
|
||||
# 要素の交換
|
||||
nums[i], nums[j] = nums[j], nums[i]
|
||||
# 基準値を 2 つの部分配列の境界へ交換する
|
||||
# ピボットを 2 つの部分配列の境界に移す
|
||||
nums[i], nums[left] = nums[left], nums[i]
|
||||
return i # 基準値のインデックスを返す
|
||||
return i # ピボットのインデックスを返す
|
||||
|
||||
def quick_sort(self, nums: list[int], left: int, right: int):
|
||||
"""クイックソート(再帰深度最適化)"""
|
||||
# 部分配列の長さが 1 なら終了
|
||||
"""クイックソート(再帰の深さの最適化)"""
|
||||
# 部分配列の長さが 1 になったら終了する
|
||||
while left < right:
|
||||
# 番兵による分割処理
|
||||
# 番兵を用いた分割操作
|
||||
pivot = self.partition(nums, left, right)
|
||||
# 2 つの部分配列のうち短いほうにクイックソートを適用する
|
||||
if pivot - left < right - pivot:
|
||||
self.quick_sort(nums, left, pivot - 1) # 左部分配列を再帰的にソート
|
||||
left = pivot + 1 # 未ソート区間の残りは [pivot + 1, right]
|
||||
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]
|
||||
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)
|
||||
print("クイックソート(中央値ピボットによる最適化)の完了後 nums =", nums1)
|
||||
|
||||
# クイックソート(再帰深度最適化)
|
||||
# クイックソート(再帰の深さの最適化)
|
||||
nums2 = [2, 4, 1, 0, 3, 5]
|
||||
QuickSortTailCall().quick_sort(nums2, 0, len(nums2) - 1)
|
||||
print("クイックソート(再帰深度最適化)完了後 nums =", nums2)
|
||||
print("クイックソート(再帰の深さの最適化)の完了後 nums =", nums2)
|
||||
|
||||
@@ -6,29 +6,29 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
def digit(num: int, exp: int) -> int:
|
||||
"""要素 num の下から k 桁目を取得(exp = 10^(k-1))"""
|
||||
# ここで高コストな累乗計算を繰り返さないよう、k ではなく exp を渡す
|
||||
"""要素 num の第 k 桁を取得する。exp = 10^(k-1) とする"""
|
||||
# k ではなく exp を渡すことで、ここで高コストのべき乗計算を繰り返すのを避ける
|
||||
return (num // exp) % 10
|
||||
|
||||
|
||||
def counting_sort_digit(nums: list[int], exp: int):
|
||||
"""計数ソート(nums の k 桁目でソート)"""
|
||||
# 10 進数の各桁は 0~9 の範囲なので、長さ 10 のバケット配列が必要
|
||||
"""カウントソート(nums の第 k 桁に基づくソート)"""
|
||||
# 十進数の各桁の範囲は 0~9 なので、長さ 10 のバケット配列が必要
|
||||
counter = [0] * 10
|
||||
n = len(nums)
|
||||
# 0~9 の各数字の出現回数を集計する
|
||||
# 0~9 の各数字の出現回数を数える
|
||||
for i in range(n):
|
||||
d = digit(nums[i], exp) # nums[i] の第 k 位を取得し、d とする
|
||||
d = digit(nums[i], exp) # nums[i] の第 k 桁を取得し、d とする
|
||||
counter[d] += 1 # 数字 d の出現回数を数える
|
||||
# 累積和を求め、「出現回数」を「配列インデックス」に変換する
|
||||
# 累積和を求め、「出現回数」を「配列のインデックス」に変換する
|
||||
for i in range(1, 10):
|
||||
counter[i] += counter[i - 1]
|
||||
# 逆順に走査し、バケット内の集計結果に従って各要素を res に格納する
|
||||
# 逆順に走査し、バケット内の集計結果に基づいて各要素を res に入れる
|
||||
res = [0] * n
|
||||
for i in range(n - 1, -1, -1):
|
||||
d = digit(nums[i], exp)
|
||||
j = counter[d] - 1 # d の配列内インデックス j を取得する
|
||||
res[j] = nums[i] # 現在の要素をインデックス j に格納する
|
||||
j = counter[d] - 1 # 配列内での d のインデックス j を取得する
|
||||
res[j] = nums[i] # 現在の要素をインデックス j に入れる
|
||||
counter[d] -= 1 # d の個数を 1 減らす
|
||||
# 結果で元の配列 nums を上書きする
|
||||
for i in range(n):
|
||||
@@ -37,12 +37,12 @@ def counting_sort_digit(nums: list[int], exp: int):
|
||||
|
||||
def radix_sort(nums: list[int]):
|
||||
"""基数ソート"""
|
||||
# 最大桁数の判定用に配列の最大要素を取得
|
||||
# 最大桁数を判定するため、配列の最大要素を取得する
|
||||
m = max(nums)
|
||||
# 下位桁から上位桁の順に走査する
|
||||
# 下位の桁から上位の桁へ順に走査する
|
||||
exp = 1
|
||||
while exp <= m:
|
||||
# 配列要素の k 桁目に対して計数ソートを行う
|
||||
# 配列の要素の第 k 桁に対してカウントソートを行う
|
||||
# k = 1 -> exp = 1
|
||||
# k = 2 -> exp = 10
|
||||
# つまり exp = 10^(k-1)
|
||||
@@ -50,7 +50,7 @@ def radix_sort(nums: list[int]):
|
||||
exp *= 10
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# 基数ソート
|
||||
nums = [
|
||||
|
||||
@@ -8,18 +8,18 @@ Author: krahets (krahets@163.com)
|
||||
def selection_sort(nums: list[int]):
|
||||
"""選択ソート"""
|
||||
n = len(nums)
|
||||
# 外側ループ:未整列区間は [i, n-1]
|
||||
# 外側のループ:未ソート区間は [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 # 最小要素のインデックスを記録
|
||||
# その最小要素を未整列区間の先頭要素と交換する
|
||||
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)
|
||||
|
||||
@@ -6,7 +6,7 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
class ArrayDeque:
|
||||
"""循環配列ベースの両端キュー"""
|
||||
"""循環配列で実装した両端キュー"""
|
||||
|
||||
def __init__(self, capacity: int):
|
||||
"""コンストラクタ"""
|
||||
@@ -15,115 +15,115 @@ class ArrayDeque:
|
||||
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 が配列の先頭を越えて前に出たら末尾に戻る
|
||||
"""循環配列のインデックスを計算する"""
|
||||
# 剰余演算を使い、配列の先頭と末尾をつなぐ
|
||||
# i が配列の末尾を越えたら、先頭に戻る
|
||||
# i が配列の先頭を越えたら、末尾に戻る
|
||||
return (i + self.capacity()) % self.capacity()
|
||||
|
||||
def push_first(self, num: int):
|
||||
"""キュー先頭にエンキュー"""
|
||||
"""先頭にエンキューする"""
|
||||
if self._size == self.capacity():
|
||||
print("両端キューがいっぱいです")
|
||||
print("両端キューが満杯です")
|
||||
return
|
||||
# 先頭ポインタを左に 1 つ移動する
|
||||
# 剰余演算により、front が配列先頭を越えた後に末尾へ戻るようにする
|
||||
# 剰余演算により、front が配列の先頭を越えたら末尾に戻るようにする
|
||||
self._front = self.index(self._front - 1)
|
||||
# num をキュー先頭に追加
|
||||
# num をキューの先頭に追加する
|
||||
self._nums[self._front] = num
|
||||
self._size += 1
|
||||
|
||||
def push_last(self, num: int):
|
||||
"""キュー末尾にエンキュー"""
|
||||
"""末尾にエンキューする"""
|
||||
if self._size == self.capacity():
|
||||
print("両端キューがいっぱいです")
|
||||
print("両端キューが満杯です")
|
||||
return
|
||||
# キュー末尾ポインタを計算し、末尾インデックス + 1 を指す
|
||||
# 末尾のインデックス + 1 を指す末尾ポインタを計算する
|
||||
rear = self.index(self._front + self._size)
|
||||
# num をキュー末尾に追加
|
||||
# num をキューの末尾に追加する
|
||||
self._nums[rear] = num
|
||||
self._size += 1
|
||||
|
||||
def pop_first(self) -> int:
|
||||
"""キュー先頭からデキュー"""
|
||||
"""先頭からデキューする"""
|
||||
num = self.peek_first()
|
||||
# 先頭ポインタを 1 つ後ろへ進める
|
||||
# 先頭ポインタを後ろに 1 つ移動する
|
||||
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)
|
||||
print("キューの先頭要素 peek_first =", peek_first)
|
||||
peek_last: int = deque.peek_last()
|
||||
print("末尾要素 peek_last =", peek_last)
|
||||
print("キューの末尾要素 peek_last =", peek_last)
|
||||
|
||||
# 要素をエンキュー
|
||||
# 要素をエンキューする
|
||||
deque.push_last(4)
|
||||
print("要素 4 を末尾に追加した後 deque =", deque.to_array())
|
||||
print("要素 4 を末尾にエンキューした後 deque =", deque.to_array())
|
||||
deque.push_first(1)
|
||||
print("要素 1 を先頭に追加した後 deque =", deque.to_array())
|
||||
print("要素 1 を先頭にエンキューした後 deque =", deque.to_array())
|
||||
|
||||
# 要素をデキュー
|
||||
# 要素をデキューする
|
||||
pop_last: int = deque.pop_last()
|
||||
print("末尾から取り出した要素 =", pop_last, "、末尾から取り出した後 deque =", deque.to_array())
|
||||
print("末尾からデキューした要素 =", pop_last, "、末尾からデキューした後 deque =", deque.to_array())
|
||||
pop_first: int = deque.pop_first()
|
||||
print("先頭から取り出した要素 =", pop_first, "、先頭から取り出した後 deque =", deque.to_array())
|
||||
print("先頭からデキューした要素 =", pop_first, "、先頭からデキューした後 deque =", deque.to_array())
|
||||
|
||||
# 両端キューの長さを取得
|
||||
# 両端キューの長さを取得する
|
||||
size: int = deque.size()
|
||||
print("両端キューの長さ size =", size)
|
||||
|
||||
# 両端キューが空かどうかを判定
|
||||
# 両端キューが空かどうかを判定する
|
||||
is_empty: bool = deque.is_empty()
|
||||
print("両端キューが空かどうか =", is_empty)
|
||||
print("両端キューは空か =", is_empty)
|
||||
|
||||
@@ -6,16 +6,16 @@ Author: Peng Chen (pengchzn@gmail.com)
|
||||
|
||||
|
||||
class ArrayQueue:
|
||||
"""循環配列ベースのキュー"""
|
||||
"""循環配列で実装したキュー"""
|
||||
|
||||
def __init__(self, size: int):
|
||||
"""コンストラクタ"""
|
||||
self._nums: list[int] = [0] * size # キュー要素を格納する配列
|
||||
self._front: int = 0 # 先頭ポインタ。先頭要素を指す
|
||||
self._nums: list[int] = [0] * size # キューの要素を格納する配列
|
||||
self._front: int = 0 # キューの先頭要素を指す先頭ポインタ
|
||||
self._size: int = 0 # キューの長さ
|
||||
|
||||
def capacity(self) -> int:
|
||||
"""キューの容量を取得"""
|
||||
"""キューの容量を取得する"""
|
||||
return len(self._nums)
|
||||
|
||||
def size(self) -> int:
|
||||
@@ -23,30 +23,30 @@ class ArrayQueue:
|
||||
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 が配列末尾を越えた後に先頭へ戻るようにする
|
||||
raise IndexError("キューが満杯です")
|
||||
# 末尾のインデックス + 1 を指す末尾ポインタを計算する
|
||||
# 剰余演算で、rear が配列末尾を越えたら先頭へ戻る
|
||||
rear: int = (self._front + self._size) % self.capacity()
|
||||
# num をキュー末尾に追加
|
||||
# num をキューの末尾に追加する
|
||||
self._nums[rear] = num
|
||||
self._size += 1
|
||||
|
||||
def pop(self) -> int:
|
||||
"""デキュー"""
|
||||
num: int = self.peek()
|
||||
# 先頭ポインタを1つ後ろへ進め、末尾を越えたら配列先頭に戻す
|
||||
# 先頭ポインタを後ろに 1 つ移動し、末尾を越えたら配列の先頭に戻す
|
||||
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]
|
||||
@@ -61,12 +61,12 @@ class ArrayQueue:
|
||||
return res
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# キューを初期化
|
||||
# キューを初期化する
|
||||
queue = ArrayQueue(10)
|
||||
|
||||
# 要素をエンキュー
|
||||
# 要素をエンキューする
|
||||
queue.push(1)
|
||||
queue.push(3)
|
||||
queue.push(2)
|
||||
@@ -74,25 +74,25 @@ if __name__ == "__main__":
|
||||
queue.push(4)
|
||||
print("キュー queue =", queue.to_list())
|
||||
|
||||
# キュー先頭の要素にアクセス
|
||||
# キューの先頭要素にアクセス
|
||||
peek: int = queue.peek()
|
||||
print("先頭要素 peek =", peek)
|
||||
print("キューの先頭要素 peek =", peek)
|
||||
|
||||
# 要素をデキュー
|
||||
# 要素をデキューする
|
||||
pop: int = queue.pop()
|
||||
print("取り出した要素 pop =", pop)
|
||||
print("取り出した後 queue =", queue.to_list())
|
||||
print("デキューした要素 pop =", pop)
|
||||
print("デキュー後 queue =", queue.to_list())
|
||||
|
||||
# キューの長さを取得
|
||||
size: int = queue.size()
|
||||
print("キューの長さ size =", size)
|
||||
|
||||
# キューが空かどうかを判定
|
||||
# キューが空か判定
|
||||
is_empty: bool = queue.is_empty()
|
||||
print("キューが空かどうか =", is_empty)
|
||||
print("キューは空か =", is_empty)
|
||||
|
||||
# 循環配列をテストする
|
||||
for i in range(10):
|
||||
queue.push(i)
|
||||
queue.pop()
|
||||
print("第", i, "回目の追加 + 取り出し後 queue = ", queue.to_list())
|
||||
print("第", i, "回のエンキューとデキューの後 queue = ", queue.to_list())
|
||||
|
||||
@@ -6,18 +6,18 @@ 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):
|
||||
@@ -31,7 +31,7 @@ class ArrayStack:
|
||||
return self._stack.pop()
|
||||
|
||||
def peek(self) -> int:
|
||||
"""スタックトップの要素にアクセス"""
|
||||
"""スタックの先頭要素にアクセスする"""
|
||||
if self.is_empty():
|
||||
raise IndexError("スタックが空です")
|
||||
return self._stack[-1]
|
||||
@@ -41,12 +41,12 @@ class ArrayStack:
|
||||
return self._stack
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# スタックを初期化
|
||||
# スタックを初期化する
|
||||
stack = ArrayStack()
|
||||
|
||||
# 要素をプッシュ
|
||||
# 要素をプッシュする
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
@@ -54,19 +54,19 @@ if __name__ == "__main__":
|
||||
stack.push(4)
|
||||
print("スタック stack =", stack.to_list())
|
||||
|
||||
# スタックトップの要素にアクセス
|
||||
# スタックの先頭要素にアクセスする
|
||||
peek: int = stack.peek()
|
||||
print("スタックトップ要素 peek =", 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)
|
||||
print("スタックは空か =", is_empty)
|
||||
|
||||
@@ -6,37 +6,37 @@ Author: Peng Chen (pengchzn@gmail.com)
|
||||
|
||||
from collections import deque
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# 両端キューを初期化
|
||||
# 両端キューを初期化する
|
||||
deq: deque[int] = deque()
|
||||
|
||||
# 要素をエンキュー
|
||||
deq.append(2) # 末尾に追加する
|
||||
# 要素をエンキューする
|
||||
deq.append(2) # キューの末尾に追加する
|
||||
deq.append(5)
|
||||
deq.append(4)
|
||||
deq.appendleft(3) # 先頭に追加する
|
||||
deq.appendleft(3) # キューの先頭に追加する
|
||||
deq.appendleft(1)
|
||||
print("両端キュー deque =", deq)
|
||||
|
||||
# 要素にアクセス
|
||||
front: int = deq[0] # 先頭要素
|
||||
print("先頭要素 front =", front)
|
||||
rear: int = deq[-1] # 末尾要素
|
||||
print("末尾要素 rear =", rear)
|
||||
# 要素にアクセスする
|
||||
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)
|
||||
# 要素をデキューする
|
||||
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)
|
||||
print("両端キューは空か =", is_empty)
|
||||
|
||||
@@ -6,17 +6,17 @@ 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 # 前駆ノードへの参照
|
||||
self.prev: ListNode | None = None # 前のノードへの参照
|
||||
|
||||
|
||||
class LinkedListDeque:
|
||||
"""双方向連結リストベースの両端キュー"""
|
||||
"""双方向連結リストで実装した両端キュー"""
|
||||
|
||||
def __init__(self):
|
||||
"""コンストラクタ"""
|
||||
@@ -25,91 +25,91 @@ class LinkedListDeque:
|
||||
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 に向ける
|
||||
# 連結リストが空の場合、front と rear の両方が node を指すようにする
|
||||
if self.is_empty():
|
||||
self._front = self._rear = node
|
||||
# 先頭へのエンキュー操作
|
||||
elif is_front:
|
||||
# node を連結リストの先頭に追加
|
||||
# node を連結リストの先頭に追加する
|
||||
self._front.prev = node
|
||||
node.next = self._front
|
||||
self._front = node # 先頭ノードを更新する
|
||||
# 末尾へのエンキュー操作
|
||||
else:
|
||||
# node を連結リストの末尾に追加
|
||||
# node を連結リストの末尾に追加する
|
||||
self._rear.next = node
|
||||
node.prev = self._rear
|
||||
self._rear = node # 末尾ノードを更新する
|
||||
self._size += 1 # キューの長さを更新
|
||||
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("両端キューが空です")
|
||||
# 要素が1つの場合、先頭と末尾は同じノードを指す
|
||||
# キューの長さが 1 のとき、先頭と末尾は同じノードを指す
|
||||
if self._size == 1:
|
||||
val: int = self._front.val
|
||||
self._front = None
|
||||
self._rear = None
|
||||
# キュー先頭からの取り出し
|
||||
# 先頭からのデキュー操作
|
||||
elif is_front:
|
||||
val: int = self._front.val # 先頭ノードの値を一時保存
|
||||
# 先頭ノードを削除
|
||||
val: int = self._front.val # 先頭ノードの値を一時保存する
|
||||
# 先頭ノードを削除する
|
||||
fnext: ListNode | None = self._front.next
|
||||
fnext.prev = None
|
||||
self._front.next = None
|
||||
self._front = fnext # 先頭ノードを更新する
|
||||
# キュー末尾からの取り出し
|
||||
# 末尾からのデキュー操作
|
||||
else:
|
||||
val: int = self._rear.val # 末尾ノードの値を一時保存
|
||||
# 末尾ノードを削除
|
||||
val: int = self._rear.val # 末尾ノードの値を一時保存する
|
||||
# 末尾ノードを削除する
|
||||
rprev: ListNode | None = self._rear.prev
|
||||
rprev.next = None
|
||||
self._rear.prev = None
|
||||
self._rear = rprev # 末尾ノードを更新する
|
||||
self._size -= 1 # キューの長さを更新
|
||||
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()):
|
||||
@@ -118,37 +118,37 @@ class LinkedListDeque:
|
||||
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)
|
||||
print("キューの先頭要素 peek_first =", peek_first)
|
||||
peek_last: int = deque.peek_last()
|
||||
print("末尾要素 peek_last =", peek_last)
|
||||
print("キューの末尾要素 peek_last =", peek_last)
|
||||
|
||||
# 要素をエンキュー
|
||||
# 要素をエンキューする
|
||||
deque.push_last(4)
|
||||
print("要素 4 を末尾に追加した後 deque =", deque.to_array())
|
||||
print("要素 4 を末尾にエンキューした後 deque =", deque.to_array())
|
||||
deque.push_first(1)
|
||||
print("要素 1 を先頭に追加した後 deque =", deque.to_array())
|
||||
print("要素 1 を先頭にエンキューした後 deque =", deque.to_array())
|
||||
|
||||
# 要素をデキュー
|
||||
# 要素をデキューする
|
||||
pop_last: int = deque.pop_last()
|
||||
print("末尾から取り出した要素 =", pop_last, "、末尾から取り出した後 deque =", deque.to_array())
|
||||
print("末尾からデキューした要素 =", pop_last, "、末尾からデキューした後 deque =", deque.to_array())
|
||||
pop_first: int = deque.pop_first()
|
||||
print("先頭から取り出した要素 =", pop_first, "、先頭から取り出した後 deque =", deque.to_array())
|
||||
print("先頭からデキューした要素 =", pop_first, "、先頭からデキューした後 deque =", deque.to_array())
|
||||
|
||||
# 両端キューの長さを取得
|
||||
# 両端キューの長さを取得する
|
||||
size: int = deque.size()
|
||||
print("両端キューの長さ size =", size)
|
||||
|
||||
# 両端キューが空かどうかを判定
|
||||
# 両端キューが空かどうかを判定する
|
||||
is_empty: bool = deque.is_empty()
|
||||
print("両端キューが空かどうか =", is_empty)
|
||||
print("両端キューは空か =", is_empty)
|
||||
|
||||
@@ -12,7 +12,7 @@ from modules import ListNode
|
||||
|
||||
|
||||
class LinkedListQueue:
|
||||
"""連結リストベースのキュー"""
|
||||
"""連結リストで実装したキュー"""
|
||||
|
||||
def __init__(self):
|
||||
"""コンストラクタ"""
|
||||
@@ -25,18 +25,18 @@ class LinkedListQueue:
|
||||
return self._size
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
"""キューが空かどうかを判定"""
|
||||
"""キューが空か判定"""
|
||||
return self._size == 0
|
||||
|
||||
def push(self, num: int):
|
||||
"""エンキュー"""
|
||||
# 末尾ノードの後ろに num を追加
|
||||
# 末尾ノードの後ろに num を追加する
|
||||
node = ListNode(num)
|
||||
# キューが空なら、先頭・末尾ノードをともにそのノードに設定
|
||||
# キューが空の場合、先頭と末尾の両方がこのノードを指すようにする
|
||||
if self._front is None:
|
||||
self._front = node
|
||||
self._rear = node
|
||||
# キューが空でなければ、そのノードを末尾ノードの後ろに追加
|
||||
# キューが空でない場合、このノードを末尾ノードの後ろに追加する
|
||||
else:
|
||||
self._rear.next = node
|
||||
self._rear = node
|
||||
@@ -45,19 +45,19 @@ class LinkedListQueue:
|
||||
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:
|
||||
@@ -66,12 +66,12 @@ class LinkedListQueue:
|
||||
return queue
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# キューを初期化
|
||||
# キューを初期化する
|
||||
queue = LinkedListQueue()
|
||||
|
||||
# 要素をエンキュー
|
||||
# 要素をエンキューする
|
||||
queue.push(1)
|
||||
queue.push(3)
|
||||
queue.push(2)
|
||||
@@ -79,19 +79,19 @@ if __name__ == "__main__":
|
||||
queue.push(4)
|
||||
print("キュー queue =", queue.to_list())
|
||||
|
||||
# キュー先頭の要素にアクセス
|
||||
# キューの先頭要素にアクセス
|
||||
peek: int = queue.peek()
|
||||
print("先頭要素 front =", peek)
|
||||
print("キューの先頭要素 front =", peek)
|
||||
|
||||
# 要素をデキュー
|
||||
# 要素をデキューする
|
||||
pop_front: int = queue.pop()
|
||||
print("取り出した要素 pop =", pop_front)
|
||||
print("取り出した後 queue =", queue.to_list())
|
||||
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)
|
||||
print("キューは空か =", is_empty)
|
||||
|
||||
@@ -12,7 +12,7 @@ from modules import ListNode
|
||||
|
||||
|
||||
class LinkedListStack:
|
||||
"""連結リストベースのスタック"""
|
||||
"""連結リストで実装したスタック"""
|
||||
|
||||
def __init__(self):
|
||||
"""コンストラクタ"""
|
||||
@@ -20,11 +20,11 @@ class LinkedListStack:
|
||||
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):
|
||||
@@ -42,13 +42,13 @@ class LinkedListStack:
|
||||
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:
|
||||
@@ -58,12 +58,12 @@ class LinkedListStack:
|
||||
return arr
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# スタックを初期化
|
||||
# スタックを初期化する
|
||||
stack = LinkedListStack()
|
||||
|
||||
# 要素をプッシュ
|
||||
# 要素をプッシュする
|
||||
stack.push(1)
|
||||
stack.push(3)
|
||||
stack.push(2)
|
||||
@@ -71,19 +71,19 @@ if __name__ == "__main__":
|
||||
stack.push(4)
|
||||
print("スタック stack =", stack.to_list())
|
||||
|
||||
# スタックトップの要素にアクセス
|
||||
# スタックの先頭要素にアクセスする
|
||||
peek: int = stack.peek()
|
||||
print("スタックトップ要素 peek =", 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)
|
||||
print("スタックは空か =", is_empty)
|
||||
|
||||
@@ -6,14 +6,14 @@ Author: Peng Chen (pengchzn@gmail.com)
|
||||
|
||||
from collections import deque
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# キューを初期化する
|
||||
# Python では通常、両端キュー deque をキューとして使う
|
||||
# queue.Queue() は正統なキュークラスだが、あまり使いやすくない
|
||||
# Python では一般に、両端キューのクラス deque をキューとして使う
|
||||
# queue.Queue() は専用のキュークラスだが、やや使いづらい
|
||||
que: deque[int] = deque()
|
||||
|
||||
# 要素をエンキュー
|
||||
# 要素をエンキューする
|
||||
que.append(1)
|
||||
que.append(3)
|
||||
que.append(2)
|
||||
@@ -21,19 +21,19 @@ if __name__ == "__main__":
|
||||
que.append(4)
|
||||
print("キュー que =", que)
|
||||
|
||||
# キュー先頭の要素にアクセス
|
||||
# キューの先頭要素にアクセス
|
||||
front: int = que[0]
|
||||
print("先頭要素 front =", front)
|
||||
print("キューの先頭要素 front =", front)
|
||||
|
||||
# 要素をデキュー
|
||||
# 要素をデキューする
|
||||
pop: int = que.popleft()
|
||||
print("取り出した要素 pop =", pop)
|
||||
print("取り出し後 que =", que)
|
||||
print("デキューした要素 pop =", pop)
|
||||
print("デキュー後 que =", que)
|
||||
|
||||
# キューの長さを取得
|
||||
size: int = len(que)
|
||||
print("キューの長さ size =", size)
|
||||
|
||||
# キューが空かどうかを判定
|
||||
# キューが空か判定
|
||||
is_empty: bool = len(que) == 0
|
||||
print("キューが空かどうか =", is_empty)
|
||||
print("キューは空か =", is_empty)
|
||||
|
||||
@@ -4,13 +4,13 @@ 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)
|
||||
@@ -18,19 +18,19 @@ if __name__ == "__main__":
|
||||
stack.append(4)
|
||||
print("スタック stack =", stack)
|
||||
|
||||
# スタックトップの要素にアクセス
|
||||
# スタックの先頭要素にアクセスする
|
||||
peek: int = stack[-1]
|
||||
print("スタックトップ要素 peek =", peek)
|
||||
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)
|
||||
print("スタックは空か =", is_empty)
|
||||
|
||||
@@ -12,108 +12,108 @@ 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 を返す
|
||||
"""インデックス i のノードの値を取得する"""
|
||||
# インデックスが範囲外の場合、空きを表す None を返す
|
||||
if i < 0 or i >= self.size():
|
||||
return None
|
||||
return self._tree[i]
|
||||
|
||||
def left(self, i: int) -> int | None:
|
||||
"""インデックス i のノードの左子ノードのインデックスを取得"""
|
||||
"""インデックス i のノードの左の子ノードのインデックスを取得する"""
|
||||
return 2 * i + 1
|
||||
|
||||
def right(self, i: int) -> int | None:
|
||||
"""インデックス i のノードの右子ノードのインデックスを取得"""
|
||||
"""インデックス i のノードの右の子ノードのインデックスを取得する"""
|
||||
return 2 * i + 2
|
||||
|
||||
def parent(self, i: int) -> int | None:
|
||||
"""インデックス i のノードの親ノードのインデックスを取得"""
|
||||
"""インデックス 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("\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)}")
|
||||
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)
|
||||
print("行きがけ順走査:", res)
|
||||
res = abt.in_order()
|
||||
print("中間順走査:", res)
|
||||
print("通りがけ順走査:", res)
|
||||
res = abt.post_order()
|
||||
print("後行順走査:", res)
|
||||
print("帰りがけ順走査:", res)
|
||||
|
||||
@@ -19,23 +19,23 @@ class AVLTree:
|
||||
self._root = None
|
||||
|
||||
def get_root(self) -> TreeNode | None:
|
||||
"""二分木の根ノードを取得"""
|
||||
"""二分木の根ノードを取得する"""
|
||||
return self._root
|
||||
|
||||
def height(self, node: TreeNode | None) -> int:
|
||||
"""ノードの高さを取得"""
|
||||
# 空ノードの高さは -1、葉ノードの高さは 0
|
||||
"""ノードの高さを取得する"""
|
||||
# 空ノードの高さは -1 、葉ノードの高さは 0
|
||||
if node is not None:
|
||||
return node.height
|
||||
return -1
|
||||
|
||||
def update_height(self, node: TreeNode | None):
|
||||
"""ノードの高さを更新する"""
|
||||
# ノードの高さは最も高い部分木の高さ + 1 に等しい
|
||||
# ノードの高さは、最も高い部分木の高さ + 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
|
||||
@@ -43,10 +43,10 @@ class AVLTree:
|
||||
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
|
||||
# child を支点として node を右回転させる
|
||||
# child を中心に node を右回転する
|
||||
child.right = node
|
||||
node.left = grand_child
|
||||
# ノードの高さを更新する
|
||||
@@ -56,10 +56,10 @@ class AVLTree:
|
||||
return child
|
||||
|
||||
def left_rotate(self, node: TreeNode | None) -> TreeNode | None:
|
||||
"""左回転"""
|
||||
"""左回転操作"""
|
||||
child = node.right
|
||||
grand_child = child.left
|
||||
# child を支点として node を左回転させる
|
||||
# child を中心に node を左回転する
|
||||
child.left = node
|
||||
node.right = grand_child
|
||||
# ノードの高さを更新する
|
||||
@@ -69,8 +69,8 @@ class AVLTree:
|
||||
return child
|
||||
|
||||
def rotate(self, node: TreeNode | None) -> TreeNode | None:
|
||||
"""回転操作を行い、この部分木の平衡を回復する"""
|
||||
# ノード node の平衡係数を取得
|
||||
"""回転操作によって、この部分木の平衡を回復する"""
|
||||
# ノード node の平衡係数を取得する
|
||||
balance_factor = self.balance_factor(node)
|
||||
# 左に偏った木
|
||||
if balance_factor > 1:
|
||||
@@ -78,7 +78,7 @@ class AVLTree:
|
||||
# 右回転
|
||||
return self.right_rotate(node)
|
||||
else:
|
||||
# 左回転してから右回転
|
||||
# 左回転してから右回転する
|
||||
node.left = self.left_rotate(node.left)
|
||||
return self.right_rotate(node)
|
||||
# 右に偏った木
|
||||
@@ -87,42 +87,42 @@ class AVLTree:
|
||||
# 左回転
|
||||
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. 挿入位置を探索してノードを挿入
|
||||
# 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. 回転操作を行い、部分木の平衡を回復する
|
||||
# 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. ノードを探索して削除
|
||||
# 1. ノードを探して削除する
|
||||
if val < node.val:
|
||||
node.left = self.remove_helper(node.left, val)
|
||||
elif val > node.val:
|
||||
@@ -130,14 +130,14 @@ class AVLTree:
|
||||
else:
|
||||
if node.left is None or node.right is None:
|
||||
child = node.left or node.right
|
||||
# 子ノード数 = 0 の場合、node をそのまま削除して返す
|
||||
# 子ノード数 = 0 の場合、node を削除して戻る
|
||||
if child is None:
|
||||
return None
|
||||
# 子ノード数 = 1 の場合、node をそのまま削除する
|
||||
# 子ノード数 = 1 の場合、node を削除する
|
||||
else:
|
||||
node = child
|
||||
else:
|
||||
# 子ノード数 = 2 の場合、中順走査の次のノードを削除し、そのノードで現在のノードを置き換える
|
||||
# 子ノード数 = 2 の場合、通りがけ順で次のノードを削除し、そのノードで現在のノードを置き換える
|
||||
temp = node.right
|
||||
while temp.left is not None:
|
||||
temp = temp.left
|
||||
@@ -145,53 +145,53 @@ class AVLTree:
|
||||
node.val = temp.val
|
||||
# ノードの高さを更新する
|
||||
self.update_height(node)
|
||||
# 2. 回転操作を行い、部分木の平衡を回復する
|
||||
# 2. 回転操作によって、この部分木の平衡を回復する
|
||||
return self.rotate(node)
|
||||
|
||||
def search(self, val: int) -> TreeNode | None:
|
||||
"""ノードを探索"""
|
||||
"""ノードを探索する"""
|
||||
cur = self._root
|
||||
# ループで探索し、葉ノードを越えたら抜ける
|
||||
# 繰り返し探索し、葉ノードを越えたら終了する
|
||||
while cur is not None:
|
||||
# 目標ノードは cur の右部分木にある
|
||||
# 目的のノードは cur の右部分木にある
|
||||
if cur.val < val:
|
||||
cur = cur.right
|
||||
# 目標ノードは cur の左部分木にある
|
||||
# 目的のノードは 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("\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("\nノード {} を削除した後の AVL 木".format(val))
|
||||
print_tree(tree.get_root())
|
||||
|
||||
# 空の AVL 木を初期化する
|
||||
avl_tree = AVLTree()
|
||||
|
||||
# ノードを挿入する
|
||||
# ノード挿入後に AVL 木がどのように平衡を保つかに注目
|
||||
# ノード挿入後、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 木がどのように平衡を保つかに注目
|
||||
# ノード削除後、AVL 木がどのように平衡を保つかに注目する
|
||||
test_remove(avl_tree, 8) # 次数 0 のノードを削除する
|
||||
test_remove(avl_tree, 5) # 次数 1 のノードを削除する
|
||||
test_remove(avl_tree, 4) # 次数 2 のノードを削除する
|
||||
|
||||
@@ -20,35 +20,35 @@ class BinarySearchTree:
|
||||
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 の右部分木にある
|
||||
# 目的のノードは cur の右部分木にある
|
||||
if cur.val < num:
|
||||
cur = cur.right
|
||||
# 目標ノードは cur の左部分木にある
|
||||
# 目的のノードは 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
|
||||
@@ -58,7 +58,7 @@ class BinarySearchTree:
|
||||
# 挿入位置は cur の左部分木にある
|
||||
else:
|
||||
cur = cur.left
|
||||
# ノードを挿入
|
||||
# ノードを挿入する
|
||||
node = TreeNode(num)
|
||||
if pre.val < num:
|
||||
pre.right = node
|
||||
@@ -66,30 +66,30 @@ class BinarySearchTree:
|
||||
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 の右部分木にある
|
||||
# 削除対象のノードは cur の右部分木にある
|
||||
if cur.val < num:
|
||||
cur = cur.right
|
||||
# 削除対象ノードは cur の左部分木にある
|
||||
# 削除対象のノードは cur の左部分木にある
|
||||
else:
|
||||
cur = cur.left
|
||||
# 削除対象ノードがなければそのまま返す
|
||||
# 削除対象のノードが存在しない場合、そのまま戻る
|
||||
if cur is None:
|
||||
return
|
||||
|
||||
# 子ノード数 = 0 or 1
|
||||
# 子ノード数 = 0 または 1
|
||||
if cur.left is None or cur.right is None:
|
||||
# 子ノード数が 0 / 1 のとき、child = null / その子ノード
|
||||
# 子ノード数 = 0 / 1 の場合、child = null / その子ノード
|
||||
child = cur.left or cur.right
|
||||
# ノード cur を削除する
|
||||
if cur != self._root:
|
||||
@@ -98,49 +98,49 @@ class BinarySearchTree:
|
||||
else:
|
||||
pre.right = child
|
||||
else:
|
||||
# 削除ノードが根ノードなら、根ノードを再設定
|
||||
# 削除するノードが根ノードの場合、根ノードを指定し直す
|
||||
self._root = child
|
||||
# 子ノード数 = 2
|
||||
else:
|
||||
# 中順走査における cur の次ノードを取得
|
||||
# 通りがけ順で cur の次のノードを取得する
|
||||
tmp: TreeNode = cur.right
|
||||
while tmp.left is not None:
|
||||
tmp = tmp.left
|
||||
# ノード tmp を再帰的に削除
|
||||
# ノード tmp を再帰的に削除する
|
||||
self.remove(tmp.val)
|
||||
# tmp で cur を上書きする
|
||||
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("\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("\nノード 16 を挿入した後の二分木\n")
|
||||
print_tree(bst.get_root())
|
||||
|
||||
# ノードを削除
|
||||
# ノードを削除する
|
||||
bst.remove(1)
|
||||
print("\nノード 1 を削除した後、二分木は\n")
|
||||
print("\nノード 1 を削除した後の二分木\n")
|
||||
print_tree(bst.get_root())
|
||||
|
||||
bst.remove(2)
|
||||
print("\nノード 2 を削除した後、二分木は\n")
|
||||
print("\nノード 2 を削除した後の二分木\n")
|
||||
print_tree(bst.get_root())
|
||||
|
||||
bst.remove(4)
|
||||
print("\nノード 4 を削除した後、二分木は\n")
|
||||
print("\nノード 4 を削除した後の二分木\n")
|
||||
print_tree(bst.get_root())
|
||||
|
||||
@@ -11,7 +11,7 @@ sys.path.append(str(Path(__file__).parent.parent))
|
||||
from modules import TreeNode, print_tree
|
||||
|
||||
|
||||
"""Driver Code"""
|
||||
"""実行用コード"""
|
||||
if __name__ == "__main__":
|
||||
# 二分木を初期化する
|
||||
# ノードを初期化する
|
||||
@@ -20,22 +20,22 @@ if __name__ == "__main__":
|
||||
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("\n二分木を初期化する\n")
|
||||
print_tree(n1)
|
||||
|
||||
# ノードの挿入と削除
|
||||
P = TreeNode(0)
|
||||
# n1 -> n2 の間にノード P を挿入
|
||||
# n1 -> n2 の間にノード P を挿入する
|
||||
n1.left = P
|
||||
P.left = n2
|
||||
print("\nノード P を挿入した後\n")
|
||||
print("\nノード P の挿入後\n")
|
||||
print_tree(n1)
|
||||
# ノードを削除
|
||||
# ノードを削除する
|
||||
n1.left = n2
|
||||
print("\nノード P を削除した後\n")
|
||||
print("\nノード P の削除後\n")
|
||||
print_tree(n1)
|
||||
|
||||
@@ -14,29 +14,29 @@ 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() # デキュー
|
||||
node: TreeNode = queue.popleft() # キューからデキューする
|
||||
res.append(node.val) # ノードの値を保存する
|
||||
if node.left is not None:
|
||||
queue.append(node.left) # 左子ノードをキューに追加
|
||||
queue.append(node.left) # 左の子ノードをエンキューする
|
||||
if node.right is not None:
|
||||
queue.append(node.right) # 右子ノードをキューに追加
|
||||
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("\n二分木を初期化する\n")
|
||||
print_tree(root)
|
||||
|
||||
# レベル順走査
|
||||
res: list[int] = level_order(root)
|
||||
print("\nレベル順走査のノード出力シーケンス = ", res)
|
||||
print("\nレベル順走査で表示するノードの順序 = ", res)
|
||||
|
||||
@@ -12,7 +12,7 @@ from modules import TreeNode, list_to_tree, print_tree
|
||||
|
||||
|
||||
def pre_order(root: TreeNode | None):
|
||||
"""先行順走査"""
|
||||
"""行きがけ順走査"""
|
||||
if root is None:
|
||||
return
|
||||
# 訪問順序:根ノード -> 左部分木 -> 右部分木
|
||||
@@ -22,44 +22,44 @@ def pre_order(root: TreeNode | None):
|
||||
|
||||
|
||||
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("\n二分木を初期化する\n")
|
||||
print_tree(root)
|
||||
|
||||
# 先行順走査
|
||||
# 行きがけ順走査
|
||||
res = []
|
||||
pre_order(root)
|
||||
print("\n先行順走査のノード出力シーケンス = ", res)
|
||||
print("\n行きがけ順走査で表示するノードの順序 = ", res)
|
||||
|
||||
# 中順走査
|
||||
# 通りがけ順走査
|
||||
res.clear()
|
||||
in_order(root)
|
||||
print("\n中間順走査のノード出力シーケンス = ", res)
|
||||
print("\n通りがけ順走査で表示するノードの順序 = ", res)
|
||||
|
||||
# 後順走査
|
||||
# 帰りがけ順走査
|
||||
res.clear()
|
||||
post_order(root)
|
||||
print("\n後行順走査のノード出力シーケンス = ", res)
|
||||
print("\n帰りがけ順走査で表示するノードの順序 = ", res)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Follow the PEP 585 - Type Hinting Generics In Standard Collections
|
||||
# PEP 585「標準コレクションにおける型ヒント用のジェネリクス」に従う
|
||||
# https://peps.python.org/pep-0585/
|
||||
from __future__ import annotations
|
||||
|
||||
# Import common libs here to simplify the code by `from module import *`
|
||||
# 共通ライブラリをここで読み込み、`from module import *` によりコードを簡潔にする
|
||||
from .list_node import (
|
||||
ListNode,
|
||||
list_to_linked_list,
|
||||
|
||||
@@ -6,15 +6,15 @@ Author: krahets (krahets@163.com)
|
||||
|
||||
|
||||
class ListNode:
|
||||
"""連結リストノードクラス"""
|
||||
"""連結リストのノードクラス"""
|
||||
|
||||
def __init__(self, val: int):
|
||||
self.val: int = val # ノード値
|
||||
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)
|
||||
@@ -24,7 +24,7 @@ def list_to_linked_list(arr: list[int]) -> ListNode | None:
|
||||
|
||||
|
||||
def linked_list_to_list(head: ListNode | None) -> list[int]:
|
||||
"""連結リストをリストにシリアライズ"""
|
||||
"""連結リストをシリアライズしてリストにする"""
|
||||
arr: list[int] = []
|
||||
while head:
|
||||
arr.append(head.val)
|
||||
|
||||
@@ -9,7 +9,7 @@ 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))
|
||||
@@ -17,7 +17,7 @@ def print_matrix(mat: list[list[int]]):
|
||||
|
||||
|
||||
def print_linked_list(head: ListNode | None):
|
||||
"""連結リストを出力"""
|
||||
"""連結リストを表示する"""
|
||||
arr: list[int] = linked_list_to_list(head)
|
||||
print(" -> ".join([str(a) for a in arr]))
|
||||
|
||||
@@ -39,10 +39,10 @@ def print_tree(
|
||||
root: TreeNode | None, prev: Trunk | None = None, is_right: bool = False
|
||||
):
|
||||
"""
|
||||
二分木を出力
|
||||
This tree printer is borrowed from TECHIE DELIGHT
|
||||
https://www.techiedelight.com/c-program-print-binary-tree/
|
||||
"""
|
||||
二分木を表示する
|
||||
この木の表示処理は TECHIE DELIGHT から借用した
|
||||
https://www.techiedelight.com/c-program-print-binary-tree/
|
||||
"""
|
||||
if root is None:
|
||||
return
|
||||
|
||||
@@ -68,14 +68,14 @@ https://www.techiedelight.com/c-program-print-binary-tree/
|
||||
|
||||
|
||||
def print_dict(hmap: dict):
|
||||
"""辞書を出力"""
|
||||
"""辞書を表示する"""
|
||||
for key, value in hmap.items():
|
||||
print(key, "->", value)
|
||||
|
||||
|
||||
def print_heap(heap: list[int]):
|
||||
"""ヒープを出力"""
|
||||
"""ヒープを表示する"""
|
||||
print("ヒープの配列表現:", heap)
|
||||
print("ヒープの木構造表示:")
|
||||
print("ヒープの木構造表現:")
|
||||
root: TreeNode | None = list_to_tree(heap)
|
||||
print_tree(root)
|
||||
|
||||
@@ -8,34 +8,34 @@ from collections import deque
|
||||
|
||||
|
||||
class TreeNode:
|
||||
"""二分木ノードクラス"""
|
||||
"""二分木のノードクラス"""
|
||||
|
||||
def __init__(self, val: int = 0):
|
||||
self.val: int = val # ノード値
|
||||
self.val: int = val # ノードの値
|
||||
self.height: int = 0 # ノードの高さ
|
||||
self.left: TreeNode | None = None # 左子ノードへの参照
|
||||
self.right: TreeNode | None = None # 右子ノードへの参照
|
||||
self.left: TreeNode | None = None # 左の子ノードへの参照
|
||||
self.right: TreeNode | None = None # 右の子ノードへの参照
|
||||
|
||||
# シリアライズの符号化規則は以下を参照:
|
||||
# https://www.hello-algo.com/chapter_tree/array_representation_of_tree/
|
||||
# 二分木の配列表現:
|
||||
# シリアライズの符号化規則は次を参照:
|
||||
# https://www.hello-algo.com/ja/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
|
||||
# 二分木の連結リスト表現:
|
||||
# /——— 15
|
||||
# /——— 7
|
||||
# /——— 3
|
||||
# | \——— 6
|
||||
# | \——— 12
|
||||
# ——— 1
|
||||
# \——— 2
|
||||
# | /——— 9
|
||||
# \——— 4
|
||||
# \——— 8
|
||||
# \——— 2
|
||||
# | /——— 9
|
||||
# \——— 4
|
||||
# \——— 8
|
||||
|
||||
|
||||
def list_to_tree_dfs(arr: list[int], i: int) -> TreeNode | None:
|
||||
"""リストを二分木にデシリアライズする: 再帰"""
|
||||
# 添字が配列長を超えるか、対応する要素が None なら、None を返す
|
||||
"""リストから二分木へデシリアライズ:再帰"""
|
||||
# 添字が配列の長さを超えるか、対応要素が None なら None を返す
|
||||
if i < 0 or i >= len(arr) or arr[i] is None:
|
||||
return None
|
||||
# 現在のノードを構築する
|
||||
@@ -47,12 +47,12 @@ def list_to_tree_dfs(arr: list[int], i: int) -> TreeNode | None:
|
||||
|
||||
|
||||
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):
|
||||
@@ -63,7 +63,7 @@ def tree_to_list_dfs(root: TreeNode, i: int, res: list[int]) -> list[int]:
|
||||
|
||||
|
||||
def tree_to_list(root: TreeNode | None) -> list[int]:
|
||||
"""二分木をリストにシリアライズする"""
|
||||
"""二分木をシリアライズしてリストにする"""
|
||||
res = []
|
||||
tree_to_list_dfs(root, 0, res)
|
||||
return res
|
||||
|
||||
@@ -11,10 +11,10 @@ class Vertex:
|
||||
|
||||
|
||||
def vals_to_vets(vals: list[int]) -> list["Vertex"]:
|
||||
"""値リスト vals を入力し、頂点リスト vets を返す"""
|
||||
"""値のリスト vals を入力し、頂点のリスト vets を返す"""
|
||||
return [Vertex(val) for val in vals]
|
||||
|
||||
|
||||
def vets_to_vals(vets: list["Vertex"]) -> list[int]:
|
||||
"""頂点リスト vets を入力し、値リスト vals を返す"""
|
||||
"""頂点のリスト vets を入力し、値のリスト vals を返す"""
|
||||
return [vet.val for vet in vets]
|
||||
|
||||
@@ -6,11 +6,11 @@ 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
|
||||
# Python コードを実行する
|
||||
for src_path in src_paths:
|
||||
process = subprocess.Popen(
|
||||
["python", src_path],
|
||||
@@ -20,14 +20,14 @@ if __name__ == "__main__":
|
||||
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")
|
||||
print(f"{len(src_paths)} 個のファイルをテストしました")
|
||||
print(f"{len(errors)} 個のファイルで例外が見つかりました")
|
||||
if len(errors) > 0:
|
||||
raise RuntimeError("\n\n".join(errors))
|
||||
|
||||
Reference in New Issue
Block a user