Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions 0230.Kth-Smallest-Element-in-a-BST/memo.md
Original file line number Diff line number Diff line change
@@ -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())を持った、名無しのインスタンス(オブジェクト)」
31 changes: 31 additions & 0 deletions 0230.Kth-Smallest-Element-in-a-BST/step1.py
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions 0230.Kth-Smallest-Element-in-a-BST/step2.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
};
30 changes: 30 additions & 0 deletions 0230.Kth-Smallest-Element-in-a-BST/step2.py
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions 0230.Kth-Smallest-Element-in-a-BST/step2_optimized.py
Original file line number Diff line number Diff line change
@@ -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