-
Notifications
You must be signed in to change notification settings - Fork 0
20. Valid Parentheses #14
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
Open
katsukii
wants to merge
4
commits into
main
Choose a base branch
from
20_parentheses
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+181
−0
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
## Problem | ||
https://leetcode.com/problems/valid-parentheses/ | ||
|
||
## Step 1 | ||
5分程度答えを見ずに考えて、手が止まるまでやってみる。 | ||
何も思いつかなければ、答えを見て解く。ただし、コードを書くときは答えを見ないこと。 | ||
動かないコードも記録する。 | ||
正解したら一旦OK。思考過程もメモする。 | ||
|
||
### Approach | ||
* 文字列を一つずつ走査。Stackに開きカッコためていき、閉じカッコが出現したらStackと突合し、適切でなければfalseを返す | ||
* 文字列が適切な並び順の場合、対応する開カッコがStackの一番上にあるはず。そうでなければfalse | ||
* もしくは、閉じカッコが来ているのにStackが空の場合はfalseを返す | ||
* 走査完了後、Stackが空であればtrue,そうでなければfalse | ||
|
||
```java | ||
class Solution { | ||
public boolean isValid(String s) { | ||
Deque<Character> stack = new ArrayDeque<>(); | ||
for (char c : s.toCharArray()) { | ||
if (c == '(' ||c == '[' ||c == '{') { | ||
stack.push(c); | ||
} | ||
if (c == ')' ||c == ']' ||c == '}') { | ||
if (stack.isEmpty()) return false; | ||
if (c == ')' && stack.pop() != '(') return false; | ||
if (c == ']' && stack.pop() != '[') return false; | ||
if (c == '}' && stack.pop() != '{') return false; | ||
} | ||
} | ||
return stack.isEmpty(); | ||
} | ||
} | ||
``` | ||
|
||
## Step 2 | ||
他の方が描いたコードを見て、参考にしてコードを書き直してみる。 | ||
参考にしたコードのリンクは貼っておく。 | ||
読みやすいことを意識する。 | ||
他の解法も考えみる。 | ||
|
||
### 参考にしたPR | ||
* https://github.com/hajimeito1108/arai60/pull/4 | ||
* https://github.com/SanakoMeine/leetcode/pull/6 | ||
* https://github.com/SanakoMeine/leetcode/pull/7 | ||
* https://github.com/olsen-blue/Arai60/pull/6 | ||
* https://github.com/canisterism/leetcode/pull/7 | ||
|
||
|
||
|
||
### 解法1. カッコを定数化した方法 | ||
* 処理の流れはStep1と同様。カッコを定数化したことにより条件分岐の数を減らせている | ||
|
||
#### 所感 | ||
* メソッドの行数は減ったものの、Step 1とどちらを選択すべきかは悩ましい | ||
* 個人的な感覚としては定数とメソッドとの間の目線の移動量が増えたことによって結果的に認知不可を上げてしまっている気がする | ||
* 何らかの事情でinputデータが変更になる可能性がある場合を考慮すると、こちらの方が管理しやすそうではある | ||
|
||
```java | ||
class Solution { | ||
private static final Set<Character> OPEN_BRACKETS = Set.of( | ||
'(', '[', '{' | ||
); | ||
|
||
private static final Map<Character, Character> BRACKET_CLOSE_OPEN = Map.of( | ||
')', '(', | ||
']', '[', | ||
'}', '{' | ||
); | ||
|
||
public boolean isValid(String s) { | ||
Deque<Character> stack = new ArrayDeque<>(); | ||
|
||
for (char c : s.toCharArray()) { | ||
if (OPEN_BRACKETS.contains(c)) { | ||
stack.push(c); | ||
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. 私は個人的にはここで continue と書いてしまうのが好みですね。 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. ありがとうございます。たしかに後続の判定必要ないのでその方がいいですね。 |
||
continue; | ||
} | ||
if (!BRACKET_CLOSE_OPEN.containsKey(c)) { | ||
continue; | ||
} | ||
if (stack.isEmpty()) { | ||
return false; | ||
} | ||
if (BRACKET_CLOSE_OPEN.get(c) != stack.pop()) { | ||
return false; | ||
} | ||
|
||
throw new IllegalArgumentException("Invalid character in input: " + c); | ||
} | ||
return stack.isEmpty(); | ||
} | ||
} | ||
``` | ||
|
||
#### 参考 | ||
カッコペアのMap定数を、{key: 開く, val: 閉じる} の順に持たせた場合の書き方 | ||
|
||
```java | ||
if (BRACKET_OPEN_CLOSE.containsKey(c)) { | ||
stack.push(c); | ||
} | ||
else if (stack.isEmpty()) | ||
{ | ||
return false; | ||
} | ||
else if (BRACKET_OPEN_CLOSE.get(stack.getLast()) == c) | ||
{ | ||
stack.pop(); | ||
} | ||
else | ||
{ | ||
return false; | ||
} | ||
``` | ||
|
||
* レビューの中で以下のようなコメントをいただいた | ||
> LinkedList を使用する場合と比べ、 ArrayDeque はどのような利点がありますか? | ||
* LinkedListは隣のノードの参照を持つため1要素のメモリ消費が大きいという認識であったが、それに加えてArrayDequeの方が高速であるということは意識できていなかった | ||
* ArrayDequeは内部的には動的配列を使っており、メモリ上で要素が連続して配置される。そのためキャッシュヒット率が高くアクセスが早い | ||
* このあたりは公式ドキュメントを読む癖をつけたい | ||
* https://docs.oracle.com/javase/8/docs/api/java/util/ArrayDeque.html | ||
|
||
|
||
### 解法2. 番兵(Sentinel) を利用する方法 | ||
* 番兵をStackの底に置いておくことで、空かどうかの事前確認が不要になるという方法 | ||
|
||
```java | ||
class Solution { | ||
private static final Set<Character> OPEN_BRACKETS = Set.of( | ||
'(', '[', '{' | ||
); | ||
|
||
private static final Map<Character, Character> BRACKET_CLOSE_OPEN = Map.of( | ||
')', '(', | ||
']', '[', | ||
'}', '{' | ||
); | ||
|
||
public boolean isValid(String s) { | ||
Deque<Character> stack = new ArrayDeque<>(); | ||
stack.push('*'); // Add the sentinel | ||
|
||
for (char c : s.toCharArray()) { | ||
if (OPEN_BRACKETS.contains(c)) { | ||
stack.push(c); | ||
} | ||
if (BRACKET_CLOSE_OPEN.containsKey(c)) { | ||
if (BRACKET_CLOSE_OPEN.get(c) != stack.pop()) { | ||
return false; | ||
} | ||
} | ||
} | ||
return stack.size() == 1; // Check whether only the sentinel remains | ||
} | ||
} | ||
``` | ||
|
||
## Step 3 | ||
今度は、時間を測りながら、もう一回書く。 | ||
アクセプトされたら消すを3回連続できたら問題はOK。 | ||
|
||
```java | ||
class Solution { | ||
public boolean isValid(String s) { | ||
Deque<Character> stack = new ArrayDeque<>(); | ||
for (char c : s.toCharArray()) { | ||
if (c == '(' ||c == '[' ||c == '{') { | ||
stack.push(c); | ||
} | ||
if (c == ')' ||c == ']' ||c == '}') { | ||
if (stack.isEmpty()) return false; | ||
if (c == ')' && stack.pop() != '(') return false; | ||
if (c == ']' && stack.pop() != '[') return false; | ||
if (c == '}' && stack.pop() != '{') return false; | ||
} | ||
} | ||
return stack.isEmpty(); | ||
} | ||
} | ||
``` |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
念のため知識面を確認させてください。 LinkedList を使用する場合と比べ、 ArrayDeque はどのような利点がありますか?
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.
正直、stackの実装にはArrayDequeを使うという形で機械的に考えていたので、LinkedListで実装できるという発想すらなかったですね。。
ご質問に対して調べずに回答すると、ArrayDequeの方がLinkedListよりメモリ効率が良いと思います。LinkedListは各要素が隣のノードの参照を持つため1要素のメモリ消費が大きいのではないかと思います。
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.
ありがとうございます。メモリ効率についてはその通りなのですが、 ArrayDeque より高速であるという点のほうが重要だと思います。詳しくは Javadoc をご覧ください。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/ArrayDeque.html
疑問に思ったときに公式ドキュメントを読む癖をつけることをお勧めいたします。
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.
公式ドキュメントに明記してあるのですね。読む癖をつけようと思います。