Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions grind75/twoSum/step1.md
Original file line number Diff line number Diff line change
@@ -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で良かった
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これは違和感が結構ありますね。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そうですよねmm
普段あまりしないのですが、がむしゃらに考えてコード書いていたら最終的によくわからないコードが誕生していましたmm
自分もかなり違和感を持つコードだったので、すぐに気付けるようにトレーニングしますmm

- One-passのHash Tableはなるほど。確かに仮に(1,5)が答えだったとして、1のときは5がハッシュテーブルになくてループが続くけど、5のときは必ず1がハッシュテーブルにあるので答えが見つかるから、インデックス順に答え合わせしながらハッシュテーブルを作れば答えを見逃すことがない。
- 髪と鉛筆でやるなら、カードをソートして、頭と尻からたどるというのもなるほど。現実世界でやろうとしたときにこういうアイデアが今は浮かばない。
51 changes: 51 additions & 0 deletions grind75/twoSum/step2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
### 課題の言語化(できたら現実世界で同じ課題を再現する)
> 数字が書かれたカードが何枚か(nums)と、壁に書かれた一つの数字(target)がある。
2枚のカードを選んで書かれている数字を足し算すると壁に書かれた数字となるようなカードの番号を見つける。
そのようなカードの組み合わせは一つしか存在せず、同じカードを2回選ぶことはできない。

一旦カードに書かれた数字が0-100だと限定し考える。
0-100の数値に対応する10x10の表のようなカード置き場を用意する。
カードを順に見ていき、壁に書かれた数字-カードに書かれた数字の数の表のマスを見て、数字がなければいま見ているカードに書かれた数字のマスにそのカードが何番目に見たカードかメモする。
順に繰り返すと、どこかで壁に書かれた数字-カードに書かれた数字の数の表のマスを見たときに数字がメモされているときがくるはず。
そのときに、マスにメモされている数字と今見ているカードが何番目かの数字が求められている答え。

### 思考記録
課題の言語化でだいぶコードのイメージがついた。
とりあえず書いてみる。
ここまでで11分。
今回は値が見付からなかったときは`[-1, -1]`で返すことにする。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javaならnullを返すのも一つかもですね

Copy link
Owner Author

@erutako erutako Jun 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かにそうですね!
nullだと呼び出し側はどんな印象を受けるのでしょうか。
個人的には戻り値が配列のシグネチャならnullは返ってこないことを期待したい(Optionalで返してほしい、nullチェックを呼び出し側にさせるのは嫌だなという感覚)ですが、[-1, -1]で返しても結局呼び出し側で値が見つかったかどうかのチェックするので、一緒ですね...
(独り言ですmm)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的には戻り値が配列のシグネチャならnullは返ってこないことを期待したい(Optionalで返してほしい、nullチェックを呼び出し側にさせるのは嫌だなという感覚)ですが、[-1, -1]で返しても結局呼び出し側で値が見つかったかどうかのチェックする

うーむ難しいですよね...正直、よく分からないです。入力が前提条件を満たさないときに特殊な値を返すとして、nullが返るのと[-1, -1]が返るのどちらがびっくりしないかという話かなと思っていて、Javaだったら前者だろうか...?と思ってコメントした感じでした。Optionalで返したいの分かります。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

びっくりしないかという捉え方すごくわかりやすいです!
そういう観点で戻りを考えることもしていこうと思いましたmm
ありがとうございます!


### 計算量
#### 時間計算量
O(N)でいける
#### 空間計算量
こちらもO(N)でいける

### コード
```Java
import java.util.Hashtable;
import java.util.Objects;

class Solution {
public int[] twoSum(int[] nums, int target) {
Hashtable<Integer, Integer> 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のほうがシンプル。
38 changes: 38 additions & 0 deletions grind75/twoSum/step3.md
Original file line number Diff line number Diff line change
@@ -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<Integer, Integer> map = new HashMap<>();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここは Map<Integer, Integer> map = new HashMap<>(); のように広いインターフェースを使ってもいいかもです。

参考: https://stackoverflow.com/questions/1348199/what-is-the-difference-between-the-hashmap-and-map-objects-in-java

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

特別な理由がない限り、広いinterfaceで定義したほうが良さそうですね!
ありがとうございますmm

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早くなった。このあたりも仕様を把握して使い分けできるようにしたい。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

もしleetcodeで出力されたRuntimeの値を見てるんでしたら、結構ブレが大きいので大きな差でなければ気にしないでいいかもです

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かにおっしゃるとおりですねmm
細かい差はあまり気にしないようにしますmm

- IDEになれているので、配列の初期化でSyntaxErrorを出したりして時間を使った。数をこなせばSyntaxErrorでつまずくところは減らしてスピードを上げられそう。
- 今日は短期記憶で頭の中に解答のコードが入ってしまったので、しばらく寝かせてまた解いてみる。