Conversation
|
|
||
| ### ① | ||
|
|
||
| 素朴に書いた。ただエッジの重みが等しいグラフ上の最短距離だから幅優先探索を思いつくのが自然な気がする (https://github.com/fhiyo/leetcode/pull/22 でも同じ状況ですぐに思いつかなかった)。 |
There was a problem hiding this comment.
自分がこの問題を読んだときは、再帰を使った深さ優先探索を思いつきました。
余裕があれば、再帰を使った深さ優先探索でも実装してみるとよいと思います。
| def minDepth(self, root: Optional[TreeNode]) -> int: | ||
| if root is None: | ||
| return 0 | ||
| min_depth = float('inf') |
There was a problem hiding this comment.
自分も整数と浮動小数を混ぜるのは違和感を感じます。ただ、もし混ぜるのであれば、 float('inf') よりは math.inf のほうがシンプルに感じます。
There was a problem hiding this comment.
float('inf')よりはmath.infのほうがシンプルに感じます。
(細かい内容の議論ではありますが...) ちなみに、nodchipさんはその2つならいつでも math.inf を選択しますか?個人的には math.inf だとinfを表すために import math を余計にやらないといけない分デメリットを感じ、避けたい気持ちがあります。今回のように何らかの理由でインポートされていれば使っても良いかなとも思いますが、やはり書き方を統一したい方が勝るんですよね...。
(NumPyにもinfがあるとかは置いておきます)
There was a problem hiding this comment.
自分の場合はおそらく import math して math.inf を使うと思います。今調べたところ、 sys.maxsize というのもあるようです。チームのメンバーから同意が得られば、そちらを使うと思います。
https://docs.python.org/ja/3/library/sys.html#sys.maxsize
There was a problem hiding this comment.
自分の場合はおそらく 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)] |
There was a problem hiding this comment.
node_with_depths でも大丈夫だと思いますし、 node_and_depths でも良いと思います。
There was a problem hiding this comment.
細かい話ですが、複数形が気になりますね。"node with depths"なら、少しおかしい気がします。良い名前かは分かりませんが、node_depth_pairsなどはより正確な名前な気がします。
There was a problem hiding this comment.
"node with depths"なら、少しおかしい気がします。
うーむなるほど。"node with depth" が複数あるという意図はきっと汲んでくれるんでしょうが、先に "node with depths" を感じるなら違和感あるでしょうね。
There was a problem hiding this comment.
意図は分かるのですが、英語としては不正確そうですよね。struct NodeWithDepthがあって、それが複数あると解釈もできますが…
| node_with_depths = [(root, 1)] | ||
| while node_with_depths: | ||
| node, depth = node_with_depths.pop() | ||
| is_leaf = True |
There was a problem hiding this comment.
変数名が動詞から始まっている点、やや違和感がありますが、 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))
| while node_with_depths: | ||
| node, depth = node_with_depths.pop() | ||
| is_leaf = True | ||
| if node.left is not None: |
There was a problem hiding this comment.
if node.left: でよいと思います。
一般に、二重否定は、読んでいて認知負荷が高いため、避けたほうが良いと思います。
| nodes.append(node) | ||
|
|
||
| def is_leaf(node: TreeNode) -> bool: | ||
| return node.left is None and node.right is None |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
個人的にはキューにノードと深さのタプルを入れていったほうがシンプルに感じます。
| depth = 0 | ||
| while nodes: | ||
| depth += 1 | ||
| num_current_depth_nodes = len(nodes) |
There was a problem hiding this comment.
現在の深さのノードの数を保持しておき、その数だけ処理する、というのは、あまり見慣れないように思います。
- キューにノードと深さのタプルを入れていく
- キューを 2 つ作り、深さごとに入れ替えていく (2nd の解法)
をよく目にします。前者の方がシンプルでよいと思います。
There was a problem hiding this comment.
一方、キューは 1 つだけ用意し、先頭から取り出す、末尾に入れるという形で実装することもあります。その場合、キューまたは別の領域に、何回辿ったかを表す整数を保存しておく必要があります。個人的には最近はこちらの書き方で書くことが多いです。
これ↑のコメントの「別の領域に」の方を念頭に書いてみたつもりだったのですが、見慣れないということはちょっと解釈違った感じですかね。「別の領域に」とおっしゃってるものだとどんな感じのコードになりますか?
There was a problem hiding this comment.
一方で、キューが一個のものと比較すると、他の方法は多少のオーバーヘッドがありそうな気もします。
キューにノードと深さのタプルを入れていく
深さが同じものが並ぶのがメモリの無駄になりますよね。
キューを 2 つ作り、深さごとに入れ替えていく (2nd の解法)
メモリアロケーションに無駄がありますよね。例えば、std::vectorを使うなら、拡張によるコピーの回数も増えそうです。
There was a problem hiding this comment.
これ↑のコメントの「別の領域に」の方を念頭に書いてみたつもりだったのですが、見慣れないということはちょっと解釈違った感じですかね。「別の領域に」とおっしゃってるものだとどんな感じのコードになりますか?
頂点情報がリストに格納されている等、頂点番号でアクセスできる場合の話になります。頂点の数を n とおいたとき、
depths = [math.inf] * n
depths[begin] = 0
のように、長さ n のリストを用意しておき、頂点 begin からの経路長を格納する、といったイメージです。
| class Solution: | ||
| def minDepth(self, root: Optional[TreeNode]) -> int: | ||
| def append_if_exists(nodes: deque[TreeNode], node: TreeNode): | ||
| if node is None: |
There was a problem hiding this comment.
コード量が少ないため、 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 |
2e66f9a to
6fcf90d
Compare
https://leetcode.com/problems/minimum-depth-of-binary-tree/description/