From 98ad076228c21c8cb8f88d2a7d9cdc7e8823c0ab Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Thu, 30 Jan 2025 23:47:11 -0500 Subject: [PATCH 1/4] create a solution --- 0020_Valid_Parentheses/solution.md | 148 +++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 0020_Valid_Parentheses/solution.md diff --git a/0020_Valid_Parentheses/solution.md b/0020_Valid_Parentheses/solution.md new file mode 100644 index 0000000..07e52eb --- /dev/null +++ b/0020_Valid_Parentheses/solution.md @@ -0,0 +1,148 @@ +## 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 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 { + public static final Set OPEN_BRACKETS = Set.of( + '(', '[', '{' + ); + + public static final Map BRACKET_CLOSE_OPEN = Map.of( + ')', '(', + ']', '[', + '}', '{' + ); + + public boolean isValid(String s) { + Deque stack = new ArrayDeque<>(); + + for (char c : s.toCharArray()) { + if (OPEN_BRACKETS.contains(c)) { + stack.push(c); + } + if (BRACKET_CLOSE_OPEN.containsKey(c)) { + if (stack.isEmpty()) { + return false; + } + if (BRACKET_CLOSE_OPEN.get(c) != stack.pop()) { + return false; + } + } + } + return stack.isEmpty(); + } +} +``` + +### 番兵(Sentinel) を利用する方法 + + +```java +class Solution { + public static final Set OPEN_BRACKETS = Set.of( + '(', '[', '{' + ); + + public static final Map BRACKET_CLOSE_OPEN = Map.of( + ')', '(', + ']', '[', + '}', '{' + ); + + public boolean isValid(String s) { + Deque 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 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(); + } +} +``` From 98b477cf2b645b595d35a860b44d722af331b341 Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Thu, 30 Jan 2025 23:52:49 -0500 Subject: [PATCH 2/4] add a supplement --- 0020_Valid_Parentheses/solution.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/0020_Valid_Parentheses/solution.md b/0020_Valid_Parentheses/solution.md index 07e52eb..04d614b 100644 --- a/0020_Valid_Parentheses/solution.md +++ b/0020_Valid_Parentheses/solution.md @@ -89,8 +89,8 @@ class Solution { } ``` -### 番兵(Sentinel) を利用する方法 - +### 解法2. 番兵(Sentinel) を利用する方法 +* 番兵をStackの底に置いておくことで、空かどうかの事前確認が不要になるという方法 ```java class Solution { From 7cafb2f7c4c4bc044af6ef8ecadd1d69f19a91ac Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Fri, 31 Jan 2025 17:43:35 -0500 Subject: [PATCH 3/4] fix --- 0020_Valid_Parentheses/solution.md | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/0020_Valid_Parentheses/solution.md b/0020_Valid_Parentheses/solution.md index 04d614b..05dec8a 100644 --- a/0020_Valid_Parentheses/solution.md +++ b/0020_Valid_Parentheses/solution.md @@ -58,11 +58,11 @@ class Solution { ```java class Solution { - public static final Set OPEN_BRACKETS = Set.of( + private static final Set OPEN_BRACKETS = Set.of( '(', '[', '{' ); - public static final Map BRACKET_CLOSE_OPEN = Map.of( + private static final Map BRACKET_CLOSE_OPEN = Map.of( ')', '(', ']', '[', '}', '{' @@ -89,16 +89,37 @@ class Solution { } ``` +#### 参考 +カッコペアの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; +} +``` + ### 解法2. 番兵(Sentinel) を利用する方法 * 番兵をStackの底に置いておくことで、空かどうかの事前確認が不要になるという方法 ```java class Solution { - public static final Set OPEN_BRACKETS = Set.of( + private static final Set OPEN_BRACKETS = Set.of( '(', '[', '{' ); - public static final Map BRACKET_CLOSE_OPEN = Map.of( + private static final Map BRACKET_CLOSE_OPEN = Map.of( ')', '(', ']', '[', '}', '{' From d8afe1e91c782df8a66f70c249c849d306783c7c Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Tue, 4 Feb 2025 13:42:28 -0500 Subject: [PATCH 4/4] add comments --- 0020_Valid_Parentheses/solution.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/0020_Valid_Parentheses/solution.md b/0020_Valid_Parentheses/solution.md index 05dec8a..9f76f34 100644 --- a/0020_Valid_Parentheses/solution.md +++ b/0020_Valid_Parentheses/solution.md @@ -74,15 +74,19 @@ class Solution { for (char c : s.toCharArray()) { if (OPEN_BRACKETS.contains(c)) { stack.push(c); + continue; } - if (BRACKET_CLOSE_OPEN.containsKey(c)) { - if (stack.isEmpty()) { - return false; - } - if (BRACKET_CLOSE_OPEN.get(c) != stack.pop()) { - return false; - } + 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(); } @@ -110,6 +114,14 @@ else } ``` +* レビューの中で以下のようなコメントをいただいた + > LinkedList を使用する場合と比べ、 ArrayDeque はどのような利点がありますか? +* LinkedListは隣のノードの参照を持つため1要素のメモリ消費が大きいという認識であったが、それに加えてArrayDequeの方が高速であるということは意識できていなかった + * ArrayDequeは内部的には動的配列を使っており、メモリ上で要素が連続して配置される。そのためキャッシュヒット率が高くアクセスが早い +* このあたりは公式ドキュメントを読む癖をつけたい + * https://docs.oracle.com/javase/8/docs/api/java/util/ArrayDeque.html + + ### 解法2. 番兵(Sentinel) を利用する方法 * 番兵をStackの底に置いておくことで、空かどうかの事前確認が不要になるという方法