-
Notifications
You must be signed in to change notification settings - Fork 0
Generate parentheses #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: arai60
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
| 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を複数形と単数系で区別するのはなんだか認知負荷がかかるなあという思いがあったのでとても参考になりました。 | ||
|
|
||
| """ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| class Solution: | ||
| def generateParenthesis(self, n: int) -> List[str]: | ||
| valid_parentheses = [] # 有効な答えを格納するコンテナ | ||
| under_processing_parentheses = [("", n, n)] # stack | ||
| while under_processing_parentheses: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. まだ閉じてない空きカッコだけ保存したらいいと思います。あとどれだけカッコが必要かは、current_parenthesから計算できます。一般にstateは少ない方がいいです。 |
||
|
|
||
| if remain_open_bracket_count == 0 and remain_close_bracket_count == 0: | ||
| valid_parentheses.append(current_parentheses) | ||
| continue | ||
|
|
||
| if 0 < remain_open_bracket_count: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 変数を比較演算子の左側に置いた方が分かりやすいです。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 私はこっちのほうが好きですね。>, >= は、私はどちらかというと否定的な意図のときに使っています。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど。
Mike0121さんが、リーダブルコードにこのアドバイスがあると言っていたのですが、場合によりけりですかね。 よくよく考えたら、私も |
||
| # 使える(があるときはいつ加えても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 | ||
| 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: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| 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) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
時間・空間計算量はいかがですか?
There was a problem hiding this comment.
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)でしょうか
There was a problem hiding this comment.
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
あ、これは知らなくていいと思います。
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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)で合ってると思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
はい, 途中で2分しないノードも出てくるのでさらに絞り込めそうではありましたがn2^nという大雑把な評価をしました。空間計算量は計算途中だけ考慮してしまいました
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
カタラン数との比較を見たら分かると思うのですが、二分木の高さは n ではなく 2n のほうが近いですね。
あと、本当に大事なのは、計算量の見積もりよりも計算時間の見積もりです。
たとえば、1分以内に計算が終わるのは n がいくつまでかを概算して、実際とすり合わせてみましょう。