Skip to content
Merged
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
116 changes: 116 additions & 0 deletions 競技プロ就活部PR用/102. Binary Tree Level Order Traversal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
15分で1回目は解けず、どのようにして同じ階層のノードを取り出して処理し、次の階層に移動するかが整理できませんでした。
各階層から値を列挙した後に、その回数分for文を回すのは、BFSの基本ができていないから思いつかなかった(考えられなかった)のかなと思います。

### 1回目
時間計算量: O(N)
空間計算量: O(N + 正解分)
```python
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []

result = []
next_level_nodes = deque()
next_level_nodes.append(root)

while next_level_nodes:
same_level_nodes = []
for i in range(len(next_level_nodes)):
node = next_level_nodes.popleft()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

popleft() すると n = len(next_level_nodes) として、時間計算量 O(n) かかってしまいます。理由は、 popleft() した場合、右側の要素を全て 1 つずつ左にずらさなければならないためです。 (CPython の実装がそのようになっているかは確認しておりません。念のため確認をお願いいたします。)

node の list を 2 つ持ち、片方を for 文で回し、もう片方に子ノードを入れていき、 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.

dequeなのでpopleftの計算量はO(1)ではないですか?

https://docs.python.org/3/library/collections.html#collections.deque

Deques are a generalization of stacks and queues (the name is pronounced “deck” and is short for “double-ended queue”). Deques support thread-safe, memory efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

list を使っていると見間違えました。失礼いたしました。

Copy link
Copy Markdown
Owner Author

@Mike0121 Mike0121 May 2, 2024

Choose a reason for hiding this comment

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

nodchipさん、ahayashiさん
ありがとうございます。listを使って、popleftなしでも書き直してみます。

実装を確認してみました。
https://github.com/python/cpython/blob/main/Modules/_collectionsmodule.c

static PyObject *
deque_popleft_impl(dequeobject *deque)
/*[clinic end generated code: output=62b154897097ff68 input=1571ce88fe3053de]*/
{
    PyObject *item;
    block *prevblock;

    if (Py_SIZE(deque) == 0) {
        PyErr_SetString(PyExc_IndexError, "pop from an empty deque");
        return NULL;
    }
    assert(deque->leftblock != NULL);
    item = deque->leftblock->data[deque->leftindex];
    deque->leftindex++;
    Py_SET_SIZE(deque, Py_SIZE(deque) - 1);
    deque->state++;

    if (deque->leftindex == BLOCKLEN) {
        if (Py_SIZE(deque)) {
            assert(deque->leftblock != deque->rightblock);
            prevblock = deque->leftblock->rightlink;
            freeblock(deque, deque->leftblock);
            CHECK_NOT_END(prevblock);
            MARK_END(prevblock->leftlink);
            deque->leftblock = prevblock;
            deque->leftindex = 0;
        } else {
            assert(deque->leftblock == deque->rightblock);
            assert(deque->leftindex == deque->rightindex+1);
            /* re-center instead of freeing a block */
            deque->leftindex = CENTER + 1;
            deque->rightindex = CENTER;
        }
    }

サイズやindexのインクリメントで、O(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.

申し訳ありません。 list で popleft() をしているのだと勘違いしました。失礼いたしました。

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.

いえ、アドバイスと確認の機会、ありがとうございました。
参考になりました。

same_level_nodes.append(node.val)

if node.left:
next_level_nodes.append(node.left)

if node.right:
next_level_nodes.append(node.right)

result.append(same_level_nodes)

return result
```


理屈が頭の中にある程度整理されていれば、余計な改行が減る気がしました。

### 2回目
```python
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []

result = []
next_level_nodes = deque()
next_level_nodes.append(root)

while next_level_nodes:
same_level_nodes = []
for _ in range(len(next_level_nodes)):
node = next_level_nodes.popleft()
same_level_nodes.append(node.val)
if node.left:
next_level_nodes.append(node.left)
if node.right:
next_level_nodes.append(node.right)

result.append(same_level_nodes)
return result
```

next_level_nodes, same_level_valuesは、next_level, same_levelでも良いかと思った。

### 3回目
```python
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []

result = []
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

resultはもう少し命名頑張っても良いかも。level_ordered_valuesとかどうでしょうか?

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.

ありがとうございます。level_ordered_values良いですね、参考にします。

next_level_nodes = deque()
next_level_nodes.append(root)
Comment on lines +72 to +73
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こうも書けますね。

Suggested change
next_level_nodes = deque()
next_level_nodes.append(root)
next_level_nodes = deque([root])

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.

存じ上げなかったです、ありがとうございます。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ドキュメントに軽く目を通して置くと良いと思います。

Returns a new deque object initialized left-to-right (using append()) with data from iterable. If iterable is not specified, the new deque is empty.

https://docs.python.org/3/library/collections.html#deque-objects


while next_level_nodes:
same_level_values = []

for _ in range(len(next_level_nodes)):
node = next_level_nodes.popleft()
same_level_values.append(node.val)
if node.left:
next_level_nodes.append(node.left)
if node.right:
next_level_nodes.append(node.right)
result.append(same_level_values)

return result
```


### 4回目 (listによる解答)
```python
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []

level_ordered_values = []
current_level_nodes = [root]

while current_level_nodes:
same_level_values = []
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

same_levelとcurrent_levelの違いは何ですか?

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.

same_level: 同じ階層(level)と判断されたノードの値
current_level: 現在注目している(各子ノードを調べている)ノードたち
で書いていました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

current_levelで統一して良さそうな気がします。
あるいは、level_ordered_values.append([])して、level_ordered_values[-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.

たしかに、分けて変数名つけるほどのメリットは感じず、nodeとvalueで分かれていれば十分ですね。
下記はたしかにおっしゃる通りですね、覚えておきます。ありがとうございます。

level_ordered_values.append([])して、level_ordered_values[-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.

私はそもそもローカル変数に current を付けておけばいい名前になっているというのが相当違和感です。current は多くの場合、context のようなグローバルな設定などに用いられる傾向が強く、今注目しているもの、というつもりだと標準的用法からかなりずれを感じます。変数名は長ければ読みやすい訳ではないんです。previous, next との対比ならばまだしも。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

確かにここでもlevel_nodes, next_level_nodesで十分通じそうですね。

Copy link
Copy Markdown
Owner Author

@Mike0121 Mike0121 May 3, 2024

Choose a reason for hiding this comment

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

odaさん、liquo-riceさん
ありがとうございます。currentは良かれと思って使っていたので、考えを修正します。
また、参考のリンクもありがとうございます。
https://source.chromium.org/search?q="current"%20-concurrent%20filepath:.*%5C.cc$

next_level_nodes = []

for i in range(len(current_level_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.

for node in current_level_nodes:

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 = current_level_nodes[i]
same_level_values.append(node.val)
if node.left:
next_level_nodes.append(node.left)
if node.right:
next_level_nodes.append(node.right)
current_level_nodes = next_level_nodes
level_ordered_values.append(same_level_values)

return level_ordered_values
```