Skip to content

111. Minimum Depth of Binary Tree#24

Open
fhiyo wants to merge 2 commits intomainfrom
111_minimum-depth-of-binary-tree
Open

111. Minimum Depth of Binary Tree#24
fhiyo wants to merge 2 commits intomainfrom
111_minimum-depth-of-binary-tree

Conversation

@fhiyo
Copy link
Copy Markdown
Owner

@fhiyo fhiyo commented Jun 20, 2024


### ①

素朴に書いた。ただエッジの重みが等しいグラフ上の最短距離だから幅優先探索を思いつくのが自然な気がする (https://github.com/fhiyo/leetcode/pull/22 でも同じ状況ですぐに思いつかなかった)。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分がこの問題を読んだときは、再帰を使った深さ優先探索を思いつきました。

余裕があれば、再帰を使った深さ優先探索でも実装してみるとよいと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6fcf90dの②で書いてみました。

def minDepth(self, root: Optional[TreeNode]) -> int:
if root is None:
return 0
min_depth = float('inf')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分も整数と浮動小数を混ぜるのは違和感を感じます。ただ、もし混ぜるのであれば、 float('inf') よりは math.inf のほうがシンプルに感じます。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

float('inf') よりは math.inf のほうがシンプルに感じます。

(細かい内容の議論ではありますが...) ちなみに、nodchipさんはその2つならいつでも math.inf を選択しますか?個人的には math.inf だとinfを表すために import math を余計にやらないといけない分デメリットを感じ、避けたい気持ちがあります。今回のように何らかの理由でインポートされていれば使っても良いかなとも思いますが、やはり書き方を統一したい方が勝るんですよね...。
(NumPyにもinfがあるとかは置いておきます)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分の場合はおそらく import math して math.inf を使うと思います。今調べたところ、 sys.maxsize というのもあるようです。チームのメンバーから同意が得られば、そちらを使うと思います。
https://docs.python.org/ja/3/library/sys.html#sys.maxsize

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分の場合はおそらく import math して math.inf を使うと思います。

なるほどありがとうございます。

今調べたところ、 sys.maxsize というのもあるようです。チームのメンバーから同意が得られば、そちらを使うと思います。

こちらはこのファイルのコメントでも言及していますが、infと比較すると有限の任意の値より大きいわけではないのでそこの弱さはあると思います。

$ python
Python 3.11.3 (main, Jun  5 2023, 00:49:13) [Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> import math
>>> 9999999999999999999999999999999999 < sys.maxsize
False
>>> 9999999999999999999999999999999999 < math.inf
True

if root is None:
return 0
min_depth = float('inf')
node_with_depths = [(root, 1)]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node_with_depths でも大丈夫だと思いますし、 node_and_depths でも良いと思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

細かい話ですが、複数形が気になりますね。"node with depths"なら、少しおかしい気がします。良い名前かは分かりませんが、node_depth_pairsなどはより正確な名前な気がします。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"node with depths"なら、少しおかしい気がします。

うーむなるほど。"node with depth" が複数あるという意図はきっと汲んでくれるんでしょうが、先に "node with depths" を感じるなら違和感あるでしょうね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

意図は分かるのですが、英語としては不正確そうですよね。struct NodeWithDepthがあって、それが複数あると解釈もできますが…

node_with_depths = [(root, 1)]
while node_with_depths:
node, depth = node_with_depths.pop()
is_leaf = True
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

変数名が動詞から始まっている点、やや違和感がありますが、 Acceptable だと思います。

また、 node.left と node.right が両方とも None のノードを見つけた時点で depth を return して early return すれば、フラグを使わずに書け、シンプルになると思います。

if not node.left and not node.right:
    return depth
if node.left
    node_with_depths.append((node.left, depth + 1))
if node.right
    node_with_depths.append((node.right, depth + 1))

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6fcf90dの①で書いてみました。

while node_with_depths:
node, depth = node_with_depths.pop()
is_leaf = True
if node.left is not None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if node.left: でよいと思います。

一般に、二重否定は、読んでいて認知負荷が高いため、避けたほうが良いと思います。

nodes.append(node)

def is_leaf(node: TreeNode) -> bool:
return node.left is None and node.right is None
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2nd のように、 return not node.left and not node.right のほうがシンプルだと思います。

def is_leaf(node: TreeNode) -> bool:
return node.left is None and node.right is None

nodes = deque()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的にはキューにノードと深さのタプルを入れていったほうがシンプルに感じます。

depth = 0
while nodes:
depth += 1
num_current_depth_nodes = len(nodes)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

現在の深さのノードの数を保持しておき、その数だけ処理する、というのは、あまり見慣れないように思います。

  • キューにノードと深さのタプルを入れていく
  • キューを 2 つ作り、深さごとに入れ替えていく (2nd の解法)

をよく目にします。前者の方がシンプルでよいと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#22 (comment)

一方、キューは 1 つだけ用意し、先頭から取り出す、末尾に入れるという形で実装することもあります。その場合、キューまたは別の領域に、何回辿ったかを表す整数を保存しておく必要があります。個人的には最近はこちらの書き方で書くことが多いです。

これ↑のコメントの「別の領域に」の方を念頭に書いてみたつもりだったのですが、見慣れないということはちょっと解釈違った感じですかね。「別の領域に」とおっしゃってるものだとどんな感じのコードになりますか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一方で、キューが一個のものと比較すると、他の方法は多少のオーバーヘッドがありそうな気もします。

キューにノードと深さのタプルを入れていく

深さが同じものが並ぶのがメモリの無駄になりますよね。

キューを 2 つ作り、深さごとに入れ替えていく (2nd の解法)

メモリアロケーションに無駄がありますよね。例えば、std::vectorを使うなら、拡張によるコピーの回数も増えそうです。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ↑のコメントの「別の領域に」の方を念頭に書いてみたつもりだったのですが、見慣れないということはちょっと解釈違った感じですかね。「別の領域に」とおっしゃってるものだとどんな感じのコードになりますか?

頂点情報がリストに格納されている等、頂点番号でアクセスできる場合の話になります。頂点の数を n とおいたとき、

depths = [math.inf] * n
depths[begin] = 0

のように、長さ n のリストを用意しておき、頂点 begin からの経路長を格納する、といったイメージです。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど。ありがとうございます 🙏

class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
def append_if_exists(nodes: deque[TreeNode], node: TreeNode):
if node is None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コード量が少ないため、 early return しなくても良いかもしれません。

if node:
    nodes.append(node)

node_with_depths = [(root, 1)]
while node_with_depths:
node, depth = node_with_depths.pop()
is_leaf = True
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私はこれ変数に置かないほうが好みですね。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants