diff --git a/grind75/twoSum/step1.md b/grind75/twoSum/step1.md new file mode 100644 index 0000000..c070061 --- /dev/null +++ b/grind75/twoSum/step1.md @@ -0,0 +1,46 @@ +初見ではないものの、時間が空いてしまったので再度STEP1からやり直す。 + +### 課題の言語化(できたら現実世界で同じ課題を再現する) +数字が書かれたカードが何枚か(nums)と、壁に書かれた一つの数字(target)がある。 +2枚のカードを選んで書かれている数字を足し算すると壁に書かれた数字となるようなカードの番号を見つける。 +そのようなカードの組み合わせは一つしか存在せず、同じカードを2回選ぶことはできない。 + +### 思考記録 +1枚のカードを固定して、残りのカードを1枚ずつ見て、足すと壁に書かれた数字になる組み合わせを探す。 +残り5分なので一旦この方針でコードを書く。 + +### 計算量 +#### 時間計算量 +2 <= nums.length <= 10^4だから、O(n^2)でも許してもらえそう。 +多分ほんとはもっと少ない計算量でいける。 +#### 空間計算量 + +### コード +```Java +class Solution { + public int[] twoSum(int[] nums, int target) { + int[] indices = new int[2]; + boolean isPairFound = false; + for (int i = 0; i < nums.length; i++) { + if (isPairFound) { + break; + } + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] == target) { + indices[0] = i; + indices[1] = j; + isPairFound = true; + break; + } + } + } + return indices; + } +} +``` + +### 感想(他の方のコードなどを読んで) +- 今回は必ず解が見つかる条件だったが、見つからないときにどのような値を返すかは考慮してよかった +- 謎にbreakで抜けてからreturnしているが、普通にその場でreturnで良かった +- One-passのHash Tableはなるほど。確かに仮に(1,5)が答えだったとして、1のときは5がハッシュテーブルになくてループが続くけど、5のときは必ず1がハッシュテーブルにあるので答えが見つかるから、インデックス順に答え合わせしながらハッシュテーブルを作れば答えを見逃すことがない。 +- 髪と鉛筆でやるなら、カードをソートして、頭と尻からたどるというのもなるほど。現実世界でやろうとしたときにこういうアイデアが今は浮かばない。 \ No newline at end of file diff --git a/grind75/twoSum/step2.md b/grind75/twoSum/step2.md new file mode 100644 index 0000000..16d37a0 --- /dev/null +++ b/grind75/twoSum/step2.md @@ -0,0 +1,51 @@ +### 課題の言語化(できたら現実世界で同じ課題を再現する) +> 数字が書かれたカードが何枚か(nums)と、壁に書かれた一つの数字(target)がある。 +2枚のカードを選んで書かれている数字を足し算すると壁に書かれた数字となるようなカードの番号を見つける。 +そのようなカードの組み合わせは一つしか存在せず、同じカードを2回選ぶことはできない。 + +一旦カードに書かれた数字が0-100だと限定し考える。 +0-100の数値に対応する10x10の表のようなカード置き場を用意する。 +カードを順に見ていき、壁に書かれた数字-カードに書かれた数字の数の表のマスを見て、数字がなければいま見ているカードに書かれた数字のマスにそのカードが何番目に見たカードかメモする。 +順に繰り返すと、どこかで壁に書かれた数字-カードに書かれた数字の数の表のマスを見たときに数字がメモされているときがくるはず。 +そのときに、マスにメモされている数字と今見ているカードが何番目かの数字が求められている答え。 + +### 思考記録 +課題の言語化でだいぶコードのイメージがついた。 +とりあえず書いてみる。 +ここまでで11分。 +今回は値が見付からなかったときは`[-1, -1]`で返すことにする。 + +### 計算量 +#### 時間計算量 +O(N)でいける +#### 空間計算量 +こちらもO(N)でいける + +### コード +```Java +import java.util.Hashtable; +import java.util.Objects; + +class Solution { + public int[] twoSum(int[] nums, int target) { + Hashtable table = new Hashtable<>(); + for (int i = 0; i < nums.length; i++) { + int requiredNum = target - nums[i]; + Integer requiredIdx = table.get(requiredNum); + if (Objects.isNull(requiredIdx)) { + table.put(nums[i], i); + } else { + return new int[]{i, requiredIdx.intValue()}; + } + } + return new int[]{-1, -1}; + } +} +``` + +### 感想(他の方のコードなどを読んで) +- 課題の言語化はハッシュテーブルありきで言語化したので、あまり意味はなかったかも?でも、ハッシュテーブルでやろうとしていることのイメージを具体化するうえでは良かった。 +- 提出コードを書ききるまでに結局40分かかった。途中でランタイムエラーがでたり、ハッシュテーブルの実装を調べたりしていたので時間の測り方としては良くなかったかも。 +- HashTableのinitialCapacityを増やして実行すると実効速度は早くならない一方で、メモリ使用量は増えるだけだった。意外。数値の範囲は割と大きいのに少しずつハッシュテーブルを大きくするほうが良いのか?実行時に与えられている数値にもよるか。 +- HashTableはスレッドセーフだったり色々サポートしてくれる分HashMapよりも遅い?nullをサポートしたい、マルチスレッド環境で動かしたいというニーズがなければHashMapで良さそう。 +- Objects.isNullを使っていたけど、containsKeyのほうがシンプル。 diff --git a/grind75/twoSum/step3.md b/grind75/twoSum/step3.md new file mode 100644 index 0000000..a01f9a1 --- /dev/null +++ b/grind75/twoSum/step3.md @@ -0,0 +1,38 @@ +### 課題の言語化(できたら現実世界で同じ課題を再現する) +step1,2でできたためSKIP + +### 思考記録 +HashMapでstep1,2の課題の言語化をイメージしながら実装する。 +さっきSTEP2を終えたばかりなので、すぐに手が動く。 + +### 計算量 +#### 時間計算量 +O(N) +#### 空間計算量 +O(N) + +### コード +```Java +class Solution { + public int[] twoSum(int[] nums, int target) { + HashMap map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int requiredVal = target - nums[i]; + if (map.containsKey(requiredVal)) { + return new int[]{i, map.get(requiredVal)}; + } + map.put(nums[i], i); + } + + // 値が見つからなかったとき + return new int[]{-1, -1}; + } +} +``` +### かかった時間 +5分26秒 + +### 感想(他の方のコードなどを読んで) +- HashTableよりHashMapのほうが1ms早くなった。このあたりも仕様を把握して使い分けできるようにしたい。 +- IDEになれているので、配列の初期化でSyntaxErrorを出したりして時間を使った。数をこなせばSyntaxErrorでつまずくところは減らしてスピードを上げられそう。 +- 今日は短期記憶で頭の中に解答のコードが入ってしまったので、しばらく寝かせてまた解いてみる。