From 8af66349c92a8a818224864a7cf7c9c227b9b66a Mon Sep 17 00:00:00 2001 From: tom4649 Date: Mon, 25 May 2026 04:44:52 +0900 Subject: [PATCH] 230. Kth Smallest Element in a BST --- 0230.Kth-Smallest-Element-in-a-BST/memo.md | 76 +++++++++++++++++++ 0230.Kth-Smallest-Element-in-a-BST/step1.py | 31 ++++++++ 0230.Kth-Smallest-Element-in-a-BST/step2.cpp | 35 +++++++++ 0230.Kth-Smallest-Element-in-a-BST/step2.py | 30 ++++++++ .../step2_optimized.py | 29 +++++++ 5 files changed, 201 insertions(+) create mode 100644 0230.Kth-Smallest-Element-in-a-BST/memo.md create mode 100644 0230.Kth-Smallest-Element-in-a-BST/step1.py create mode 100644 0230.Kth-Smallest-Element-in-a-BST/step2.cpp create mode 100644 0230.Kth-Smallest-Element-in-a-BST/step2.py create mode 100644 0230.Kth-Smallest-Element-in-a-BST/step2_optimized.py diff --git a/0230.Kth-Smallest-Element-in-a-BST/memo.md b/0230.Kth-Smallest-Element-in-a-BST/memo.md new file mode 100644 index 0000000..8f72bc4 --- /dev/null +++ b/0230.Kth-Smallest-Element-in-a-BST/memo.md @@ -0,0 +1,76 @@ +# 230. Kth Smallest Element in a BST + +## step1 +in-orderで処理をすれば良い。再帰かループが考えられるが、まず解くことを目指して書きやすい再帰を選択。 + +11mぐらいで解けたが、nonlocalをつけずに一度間違えた。これは大きなミスだと思うので反省。変数のスコープは何度か確認したがまた間違えたので再度確認する。 + +### 変数のスコープ +PythonはLEGBルールに基づいて変数を探す。しかし、関数内に代入文があるとPythonはLocal変数であると解釈しEnclosingに変数を探しに行かなくなるため、nonlocal (またはglobal) 宣言が必要となる。mutableな変数の破壊的処理の場合にはnonlocalは不要。 + +> ある名前がコードブロック内のどこかで束縛操作されていたら、そのブロック内で使われるその名前はすべて、現在のブロックへの参照として扱われます。このため、ある名前がそのブロック内で束縛される前に使われるとエラーにつながります。この規則は敏感です。Python には宣言がなく、コードブロックのどこでも名前束縛操作ができます。あるコードブロックにおけるローカル変数は、ブロックのテキスト全体から名前束縛操作を走査することで決定されます。例は UnboundLocalError についての FAQ 項目 を参照してください。 + +https://docs.python.org/ja/3.11/reference/executionmodel.html#naming-and-binding + +## step2 + +ループでも書く + +> Follow up: If the BST is modified often (i.e., we can do insert and delete operations) and you need to find the kth smallest frequently, how would you optimize? + +わからないのでAIに聞いてみる: + +真の最適解:「各ノードに、自分を根とする部分木の総ノード数(size)を持たせる(Augmented BST)」です。 + +データ構造を以下のように拡張します。 + +```python +class ImprovedTreeNode: + + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + self.size = 1 # 自分 + 左部分木のノード数 + 右部分木のノード数 +``` +sizeがあればこの問題は簡単になる。 +sizeの変更はBSTの場合簡単なので、頻繁にinsertやdeleteが行われる場合に適していそう。 +## 他の人のコード + +https://github.com/thonda28/leetcode/pull/8 + +> インスタンス変数を使う場合はスレッドセーフでなくなる + +ローカル変数であれば、スタック領域に変数が置かれるので、スレッドセーフである。 + +しかし、インスタンス変数の場合にはヒープに置かれるので、メモリ区間を共有するスレッドで共有される。 + +全く意識していなかったので勉強になった。 + +### スタックに積む書き方 +こんな書き方もできるのか。 + +```python +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + inorder_stack = [] + + while True: + while root: + inorder_stack.append(root) + root = root.left + + root = inorder_stack.pop() + k -= 1 + if k == 0: + return root.val + + root = root.right +``` + + +C++でも書いてみる。ラムダ式を書けなかった。 + +auto 変数名 = [キャプチャ] (引数) -> 戻り値の型(省略可) {処理}; + +C++におけるラムダ式のは、関数に見えて実は「関数のように呼び出せる機能(operator())を持った、名無しのインスタンス(オブジェクト)」 diff --git a/0230.Kth-Smallest-Element-in-a-BST/step1.py b/0230.Kth-Smallest-Element-in-a-BST/step1.py new file mode 100644 index 0000000..abfe63e --- /dev/null +++ b/0230.Kth-Smallest-Element-in-a-BST/step1.py @@ -0,0 +1,31 @@ +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def kthSmallest(self, root: TreeNode | None, k: int) -> int: + if root is None: + raise ValueError("root is None") + + num_seen = 0 + kth_smallest = None + + def traverse(node: TreeNode) -> None: + nonlocal kth_smallest, num_seen + if node.left is not None: + traverse(node.left) + if kth_smallest is not None: + return + num_seen += 1 + if num_seen == k: + kth_smallest = node.val + return + if node.right is not None: + traverse(node.right) + + traverse(root) + return kth_smallest diff --git a/0230.Kth-Smallest-Element-in-a-BST/step2.cpp b/0230.Kth-Smallest-Element-in-a-BST/step2.cpp new file mode 100644 index 0000000..f5dce2a --- /dev/null +++ b/0230.Kth-Smallest-Element-in-a-BST/step2.cpp @@ -0,0 +1,35 @@ +// Definition for a binary tree node. +struct TreeNode { + int val; + TreeNode *left; + TreeNode *right; + TreeNode() : val(0), left(nullptr), right(nullptr) {} + TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} +}; + +class Solution { + public: + int kthSmallest(TreeNode *root, int k) { + int k_th_smallest = -1; + int num_seen = 0; + auto traverse = [k, &k_th_smallest, &num_seen](auto &self, TreeNode *node) -> void { + if (!node) { + return; + } + self(self, node->left); + if (k_th_smallest >= 0) { + return; + } + num_seen++; + if (num_seen== k){ + k_th_smallest = node->val; + return; + } + self(self, node->right); + }; + + traverse(traverse, root); + return k_th_smallest; + } +}; diff --git a/0230.Kth-Smallest-Element-in-a-BST/step2.py b/0230.Kth-Smallest-Element-in-a-BST/step2.py new file mode 100644 index 0000000..238ad2c --- /dev/null +++ b/0230.Kth-Smallest-Element-in-a-BST/step2.py @@ -0,0 +1,30 @@ +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + + +class Solution: + def kthSmallest(self, root: TreeNode | None, k: int) -> int: + if root is None: + raise ValueError("root is None") + + num_seen = 0 + stack = [(root, False)] + + while stack: + node, seen_left = stack.pop() + if node is None: + continue + if seen_left: + num_seen += 1 + if num_seen == k: + return node.val + stack.append((node.right, False)) + continue + stack.append((node, True)) + stack.append((node.left, False)) + + return None diff --git a/0230.Kth-Smallest-Element-in-a-BST/step2_optimized.py b/0230.Kth-Smallest-Element-in-a-BST/step2_optimized.py new file mode 100644 index 0000000..e8ea03e --- /dev/null +++ b/0230.Kth-Smallest-Element-in-a-BST/step2_optimized.py @@ -0,0 +1,29 @@ +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + self.size = 1 + + +class Solution: + def kthSmallest(self, root: TreeNode | None, k: int) -> int: + if root is None: + raise ValueError("root is None") + + target = k + node = root + + while node is not None: + left_size = node.left.size if node.left else 0 + + if target == left_size + 1: + return node.val + elif target <= left_size: + node = node.left + else: + target -= left_size + 1 + node = node.right + + return None