-
Notifications
You must be signed in to change notification settings - Fork 0
20 valid_parentheses.md #21
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
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,227 @@ | ||
20. Valid Parentheses | ||
|
||
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. | ||
|
||
An input string is valid if: | ||
|
||
Open brackets must be closed by the same type of brackets. | ||
Open brackets must be closed in the correct order. | ||
Every close bracket has a corresponding open bracket of the same type. | ||
|
||
# Step 1 | ||
|
||
とりあえずアクセプトされるコードを書く | ||
|
||
Stackを使えば解けるのはわかる | ||
|
||
Pythonでstackを使うには、listかdequeかの二択になりそう | ||
|
||
まずはモジュールのかかわらないlistでやってみよう | ||
|
||
何回かエラーが出たため、複数回提出してAC | ||
|
||
```python | ||
|
||
class Solution: | ||
def isValid(self, s: str) -> bool: | ||
still_open_brackets = [] | ||
bracket_pairs = {"(": ")", "{": "}", "[": "]"} | ||
for ch in s: | ||
if ch in ("(", "{", "["): | ||
still_open_brackets.append(ch) | ||
else: | ||
try: | ||
most_recent_open_bracket = still_open_brackets.pop(-1) | ||
except: | ||
return False | ||
if ch != bracket_pairs[most_recent_open_bracket]: | ||
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. bracket_pairs が open_to_close のような命名だと、 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. そうかもしれません。ただ、bracketという単語が入らないと言葉足らずな気がしています。ありがとうございます。 |
||
return False | ||
if len(still_open_brackets) > 0: | ||
return False | ||
return True | ||
|
||
``` | ||
|
||
# Step 2 | ||
|
||
ほかの人のPRを読みコード改善 | ||
|
||
Fuminitonさんのプルリクが勉強になった | ||
会話が多いため | ||
https://github.com/Fuminiton/LeetCode/pull/6/files | ||
|
||
https://github.com/KTakao01/leetcode/pull/5/files | ||
|
||
いくつか変数名変えたり、同値な変形をしています。 | ||
|
||
しかし、今回のcontinueは個人的にはあんまりわかりやすくない気がしています。 | ||
|
||
if bracket in "({["とそうでない場合は対等なイメージのため。 | ||
|
||
|
||
|
||
```python | ||
|
||
class Solution: | ||
def isValid(self, s: str) -> bool: | ||
still_open_brackets = [] | ||
bracket_pairs = {"(": ")", "{": "}", "[": "]"} | ||
for bracket in s: | ||
if bracket in "({[": | ||
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. "({["はbracket_pairs.keys()でもとれるにもかかわらず、あえてここで再度"({["を定義した理由をお聞きしたいです。 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. 三つしか要素がないので、ベタ打ちのほうが分かりやすいかなと思いました。 |
||
still_open_brackets.append(bracket) | ||
continue | ||
if len(still_open_brackets) == 0: | ||
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. 細かいですが、listが空であるかを表すには if not を使うほうが一般的なようです。
https://peps.python.org/pep-0008/#:~:text=,that%20empty%20sequences%20are%20false 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. ありがとうございます!!こんなこともPEP8に書いてあるんですね。 |
||
return False | ||
if bracket != bracket_pairs[still_open_brackets.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. 「bracketと、スタックから要素を取り出して要素を変換したものを比較し、その結果を評価している」 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. 動詞の数ぶん処理を分けるというのはいい基準ですね。ありがとうございます。 |
||
return False | ||
return len(still_open_brackets) == 0 | ||
|
||
``` | ||
|
||
次に、dequeの練習をします。 | ||
|
||
まずdequeとは何か?右からも左からもpopできる便利なデータ構造という理解ですが、ドキュメント的にはこういうことらしい。 | ||
|
||
「このモジュールは、汎用の Python 組み込みコンテナ dict, list, set, および tuple に代わる、特殊なコンテナデータ型を実装しています。」 | ||
|
||
とくにdequeは、「両端における append や pop を高速に行えるリスト風のコンテナ」 | ||
|
||
https://docs.python.org/ja/3.13/library/collections.html | ||
|
||
ソーソコードがようやくわかった | ||
|
||
私が | ||
from collections import deque | ||
と書くとき、まずpythonのcollectionsモジュールを参照して、それがCで書かれた_collectionsモジュールを参照するという建付けらしい。 | ||
|
||
結構大きな発見 | ||
|
||
https://github.com/python/cpython/blob/3.13/Lib/collections/__init__.py | ||
|
||
https://github.com/python/cpython/blob/3.13/Modules/_collectionsmodule.c | ||
|
||
バグもなく一発AC | ||
|
||
```python | ||
|
||
class Solution: | ||
from collections import deque | ||
|
||
def isValid(self, s: str) -> bool: | ||
open_brackets = deque() | ||
bracket_pairs = {"(": ")", "{": "}", "[": "]"} | ||
for bracket in s: | ||
if bracket in "({[": | ||
open_brackets.append(bracket) | ||
continue | ||
try: | ||
recent_open_bracket = open_brackets.pop() | ||
if bracket != bracket_pairs[recent_open_bracket]: | ||
return False | ||
except: | ||
return False | ||
return len(open_brackets) == 0 | ||
|
||
``` | ||
|
||
ところで、ChatGPTに聞いて面白い発見をしました | ||
|
||
まず解答 | ||
|
||
```python | ||
|
||
from collections import deque | ||
|
||
class Solution: | ||
def isValid(self, s: str) -> bool: | ||
# 括弧の対応関係を定義(閉じ括弧をキー、対応する開き括弧を値として) | ||
mapping = {')': '(', '}': '{', ']': '['} | ||
stack = deque() | ||
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. 名前と入っているものがずれているので違和感を感じます。 |
||
|
||
for char in s: | ||
# 開き括弧の場合はstackに追加 | ||
if char in mapping.values(): | ||
stack.append(char) | ||
# 閉じ括弧の場合 | ||
elif char in mapping: | ||
# stackが空であったり、直前の開き括弧と対応していなければ無効 | ||
if not stack or stack.pop() != mapping[char]: | ||
return False | ||
else: | ||
# 問題文にない文字が現れた場合(本問題では発生しない前提) | ||
return False | ||
|
||
# 全ての括弧が正しく閉じられていればstackは空になっている | ||
return not stack | ||
|
||
``` | ||
|
||
この | ||
if not stack or stack.pop() != mapping[char]: | ||
の部分でpopができないとエラーが出るのではないかと思って聞いたところ、 | ||
|
||
「Pythonでは、論理演算子orは短絡評価(short-circuit evaluation)を行うため、 | ||
左側の条件がTrueの場合、右側の条件は評価されません。つまり、 | ||
if not stack or stack.pop() != mapping[char]:の部分では、 | ||
stackが空の場合、not stackがTrueとなり、stack.pop()は実行されず、 | ||
そのままTrueとしてreturn Falseが実行されます。これにより、 | ||
空のスタックに対してpop()を呼び出してエラーが発生することを防いでいます。」 | ||
|
||
とのことです。 | ||
|
||
|
||
# Step 3 | ||
|
||
三回解きます。 | ||
|
||
```python | ||
|
||
class Solution: | ||
def isValid(self, s: str) -> bool: | ||
open_brackets = [] | ||
bracket_pairs = {"(": ")", "{": "}", "[": "]"} | ||
for bracket in s: | ||
if bracket in "({[": | ||
open_brackets.append(bracket) | ||
elif not open_brackets or bracket_pairs[open_brackets.pop()] != bracket: | ||
return False | ||
return not open_brackets | ||
|
||
``` | ||
|
||
elif not open_brackets or bracket_pairs[open_brackets.pop()] != bracket: | ||
の行を読むのにかなり負荷がかかるが、これは私の読解力の問題かな? | ||
コードの構造としては、これが美しいと思うけど。 | ||
|
||
考えたのですが、やっぱり私としてはこれが「覚えられるコード」ということで、これを写経することにしました。 | ||
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. その行、3つのことをしているんですよね。
if bracket in "({[":
open_brackets.append(bracket)
continue
if not open_brackets:
return False
if bracket_pairs[open_brackets[-1]] != bracket:
return False
open_brackets.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. そうですね。 if bracket_pairs[open_brackets[-1]] != bracket: これ口頭の面接だったらイエローカードですね... 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. まあ、[-1] でなくても単に変数においてもいいです。pop の仕方が変わるくらいで。 if not open_brackets:
return False
opening = open_brackets.pop()
if bracket_pairs[opening] != bracket:
return False いやー、まあ、これはイエローにはならないんじゃないかなと思います。(面接という極端な状況では、見落としや混乱は当然にあるよね、くらいの感覚です。) たぶん、医師国家試験の禁忌肢に近い感覚です。「そんな侵襲性の高い手技を一切検査もせずに始めるのか?」とか。 |
||
|
||
```python | ||
|
||
class Solution: | ||
def isValid(self, s: str) -> bool: | ||
open_brackets = [] | ||
bracket_pairs = {"(": ")", "{": "}", "[": "]"} | ||
for bracket in s: | ||
if bracket in "({[": | ||
open_brackets.append(bracket) | ||
elif not open_brackets or bracket != bracket_pairs[open_brackets.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.
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. ありがとうございます。 |
||
return False | ||
return not open_brackets | ||
|
||
``` | ||
|
||
```python | ||
|
||
class Solution: | ||
def isValid(self, s: str) -> bool: | ||
open_brackets = [] | ||
bracket_pairs = {"(": ")", "{": "}", "[": "]"} | ||
for bracket in s: | ||
if bracket in "({[": | ||
open_brackets.append(bracket) | ||
continue | ||
if not open_brackets or bracket != bracket_pairs[open_brackets.pop()]: | ||
return False | ||
return not open_brackets | ||
|
||
``` |
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.
何も書いていない except は ^C で送られてくるシグナルさえ捕まえます。
タイミングよく ^C を送ることで、括弧の対応関係を変えられるプログラム、セキュリティー的にもまずそうではないですか。
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.
あ、exceptって普通エラーの内容を書いたほうが良いんですね。
チュートリアル「Exceptionはほぼすべての例外を捕捉するワイルドカードとして使えます。しかし良い例外処理の手法とは、処理対象の例外の型をできる限り詳細に書き、予期しない例外はそのまま伝わるようにすることです。」(https://docs.python.org/ja/3.13/tutorial/errors.html#handling-exceptions)
では、
except IndexError:
にしましょうか。
しかし、そもそもtry-except文の存在意義は
「部分的にバグっててもプログラムが止まらないようにして、それとは別にバグへの対応も出来るようにする」
ということだと思うので、条件分岐で対応できるところにtry-exceptを使うのは思想がおかしかったかもしれません。