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
31 changes: 31 additions & 0 deletions generate_parentheses/phase1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
valid_parentheses = []
under_processing_parentheses = ["("]
while under_processing_parentheses:
current_parenthesis = under_processing_parentheses.pop()
open_bracket_count = current_parenthesis.count("(")
closed_bracket_count = current_parenthesis.count(")")

add_parentheses = []
if closed_bracket_count == open_bracket_count:
# closed_bracket_count == open_bracket_countな状態でclosed_bracketを追加するとinvalidな状態になる
add_parentheses.append(current_parenthesis + "(")
elif open_bracket_count == n:
# open_bracketを使い切ったら文字列の長さが2*nになるまでclosed_bracketを追加
add_parentheses.append(current_parenthesis + ")")
else:
# それ以外はどのbracketを使い切ってもvalidな状態になりえる途中経過状態になる
add_parentheses.append(current_parenthesis + "(")
add_parentheses.append(current_parenthesis + ")")

for add_parenthesis in add_parentheses:
if len(add_parenthesis) == 2*n:
# terminate
valid_parentheses.append(add_parenthesis)

else:
# 追加
under_processing_parentheses.append(add_parenthesis)

return valid_parentheses
30 changes: 30 additions & 0 deletions generate_parentheses/phase2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
valid_parentheses = []
under_processing_parentheses = [("", n, n)] #initial state

while under_processing_parentheses:
current_parentheses, remain_open_bracket_count, remain_close_bracket_count = under_processing_parentheses.pop()
if remain_open_bracket_count == 0 and remain_close_bracket_count == 0:
valid_parentheses.append(current_parentheses)
continue

if 0 < remain_open_bracket_count:
under_processing_parentheses.append((current_parentheses + "(", remain_open_bracket_count - 1, remain_close_bracket_count))
if remain_open_bracket_count < remain_close_bracket_count:
under_processing_parentheses.append((current_parentheses + ")", remain_open_bracket_count, remain_close_bracket_count - 1))

return valid_parentheses

"""
phase1と同じようにvalidな状態から終了状態までの遷移グラフを追うようなコードにした

Reference:
https://github.com/ryoooooory/LeetCode/pull/6/files
refactoredSolution2.javaが近いのかなと感じた。

https://github.com/shining-ai/leetcode/pull/53/files
再帰で解く解き方も確かにできますね。level4のコードが綺麗ですし, 僕のphase1のコードで感じていた
add_parenthesisとadd_parenthesesを複数形と単数系で区別するのはなんだか認知負荷がかかるなあという思いがあったのでとても参考になりました。

"""
20 changes: 20 additions & 0 deletions generate_parentheses/phase3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Solution:
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.

ちょっと大雑把な考察になってしまったのですが, 状態をグラフで考えた時, 深さnまでは状態数が各状態ノードから大体2個ずつ伸びるので深さnまでの状態数の合計はΣ_{k}2^k, それ以降は状態数は各状態ノードから1個しか伸びないので終了状態になる深さ2nまでの状態数の合計はn2^nなので, 全てのノードを走査するのにかかる時間計算量がO(n2^n), 今回はDFSを行っているので空間計算量がO(n)でしょうか

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

出力サイズはだいたいカタラン数ですね。
https://en.wikipedia.org/wiki/Catalan_number

a(n) ~ 4^n / (sqrt(Pi * n) * (n + 1))
https://oeis.org/A000108

あ、これは知らなくていいと思います。

Copy link
Copy Markdown

@liquo-rice liquo-rice Apr 24, 2024

Choose a reason for hiding this comment

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

カタラン数の予備知識なしなら、大体O(n * 2 ^ n)で抑えられそうという見積もりですかね。高さnのbinary treeのノード数がO(2 ^ n)で、最後にstringを生成でO(n)で、合わせてO(n * 2 ^ n)。

空間計算量も出力を含めないなら、O(n)で合ってると思います。

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.

はい, 途中で2分しないノードも出てくるのでさらに絞り込めそうではありましたがn2^nという大雑把な評価をしました。空間計算量は計算途中だけ考慮してしまいました

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 ではなく 2n のほうが近いですね。

あと、本当に大事なのは、計算量の見積もりよりも計算時間の見積もりです。
たとえば、1分以内に計算が終わるのは n がいくつまでかを概算して、実際とすり合わせてみましょう。

def generateParenthesis(self, n: int) -> List[str]:
valid_parentheses = [] # 有効な答えを格納するコンテナ
under_processing_parentheses = [("", n, n)] # stack
while under_processing_parentheses:
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_parentheses, remain_open_bracket_count, remain_close_bracket_count = under_processing_parentheses.pop()
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_parenthesから計算できます。一般にstateは少ない方がいいです。
名前はnum_unclosed_parenthesesとかどうでしょう。


if remain_open_bracket_count == 0 and remain_close_bracket_count == 0:
valid_parentheses.append(current_parentheses)
continue

if 0 < remain_open_bracket_count:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

変数を比較演算子の左側に置いた方が分かりやすいです。remain_open_bracket_count > 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私はこっちのほうが好きですね。>, >= は、私はどちらかというと否定的な意図のときに使っています。
index >= len(array)
など。

Copy link
Copy Markdown

@liquo-rice liquo-rice Apr 24, 2024

Choose a reason for hiding this comment

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

なるほど。

変数を比較演算子の左側に置いた方が分かりやすいです。

Mike0121さんが、リーダブルコードにこのアドバイスがあると言っていたのですが、場合によりけりですかね。

よくよく考えたら、私もr < 0 || NUM_ROWS <= rみたいに書いたりする(小さい順に書きたい)ので、特に決まったルールはなさそうです。

# 使える(があるときはいつ加えてもvalidなparenthesesになる
under_processing_parentheses.append((current_parentheses + "(", remain_open_bracket_count - 1, remain_close_bracket_count))

if remain_open_bracket_count < remain_close_bracket_count:
# remain_open_bracket_count < remain_close_bracket_count の時だけvalidなparenthesesが作れる
under_processing_parentheses.append((current_parentheses + ")",remain_open_bracket_count, remain_close_bracket_count - 1))

return valid_parentheses
38 changes: 38 additions & 0 deletions generate_parentheses/phase4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
# 状態を格納する変数を少なくするアプローチ
valid_parentheses = [] # 有効な答えを格納するコンテナ
under_processing_parentheses = [""] # stack
while under_processing_parentheses:
current_parentheses = under_processing_parentheses.pop()

if current_parentheses.count("(") == n and current_parentheses.count(")") == n:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

len(current_parentheses) == 2 * nでできそうです。

valid_parentheses.append(current_parentheses)
continue

if current_parentheses.count("(") < n:
# 使える(があるときはいつ加えてもvalidなparenthesesになる
under_processing_parentheses.append(current_parentheses + "(")

if current_parentheses.count(")") < current_parentheses.count("("):
under_processing_parentheses.append(current_parentheses + ")")

return valid_parentheses

# 再帰で書く場合

class Solution:
def generateParenthesis(self, n: int) -> List[str]:
def _generate_valid_parentheses(current_parenthesis: str, n: int)->List[str]:
if current_parenthesis.count("(") == n and current_parenthesis.count(")") == n:
return [current_parenthesis]

valid_parentheses = []
if current_parenthesis.count("(") < n:
valid_parentheses += _generate_valid_parentheses(current_parenthesis + "(", n)
if current_parenthesis.count(")") < current_parenthesis.count("("):
valid_parentheses += _generate_valid_parentheses(current_parenthesis + ")", n)

return valid_parentheses

return _generate_valid_parentheses("", n)