Skip to content
Open
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
103 changes: 103 additions & 0 deletions 1/1. TWO SUM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# [1. TWO SUM](https://leetcode.com/problems/two-sum/description/)

## Step1
### 問題意図の考察

- 整数配列 numsと整数 targetに対して、合計がtargetになる数のindexを返す
- 問題文にある条件の確認
1. 必ずちょうど一つの解が存在する
2. 同じ要素を返してはいけない
3. インデックスを返す順番はどちらでもいい
4. 1.exactly one solution -> 「複数解をどう扱うか」を考えなくでOK、つまり即return

- 制約の確認
1. 配列numsの長さ、要素数は、2 ~ 10,000 -> 最大要素数は10,000で単純ループだと1億回
2. 配列numsの各要素nums[i]は、-1,000,000,000 ~ +1,000,000,000
-> 大きな数や小さな数が出てくることもあるから値に依存したアルゴリズムは不向きかな
-> C++ int(32bits)は、おおよそ-2,174,483,648 ~ -2,174,483,647、nums[i]は+-10^9なので合計もintの範囲内でintOK。オーバーフローはしない,問題文通り
3. targetの値も、+-10^9の間、intOK
- ここでは「1億回の比較O(n^2)」くらいだと、0.05 ~ 0.3秒前後でそんなに時間が掛からないが、よりベターな方法を探したい。
- ペアを探す。つまり、補数を探す。O(1)での探索
-> nums[i] + nums[j] = target
-> nums[j] = target - nums[i]
- 値ではなく、indexで返せるか

### 解法を考える。
- 整数配列nums、整数targetで、nums[i] + nums[j] = targetとなるインデックスi, jを探す
- 問題文から補数を探す、今回はハッシュマップ unordered_mapで良さそう
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

std::unordered_map (std::unordered_set) は std::map (std::set) より insert() が遅いようです。また、要素数が少ない時は std::unordered_map (std::unordered_set) のほうがメモリ使用量が大きいようです。
https://chromium.googlesource.com/chromium/src/+/HEAD/base/containers/README.md#std_unordered_map-and-std_unordered_set
https://groups.google.com/a/chromium.org/g/chromium-dev/c/rdxOHKzQmRY?pli=1
用途に応じて適切に使い分けるのが理想的です。一方、処理時間があまり問題とならない場合に、データ構造の選択に時間をかけるのは不適切です。このあたりはチームの平均的なやり方に合わせることをおすすめします

Copy link
Copy Markdown
Owner Author

@Apo-Matchbox Apo-Matchbox Nov 14, 2025

Choose a reason for hiding this comment

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

@nodchip
コメント頂きありがとうございます。
ドキュメントもう少しじっくり読んでみます。
適切な評価をして、使い分けれるように今はじっくりと考えていきたいと思いますが、

一方、処理時間があまり問題とならない場合に、データ構造の選択に時間をかけるのは不適切です。このあたりはチームの平均的なやり方に合わせることをおすすめします

この辺りも頭の片隅に記憶しておきます。

- 整理すると、
1. 配列を左から順番に要素をみる
2. nums[j] = target - nums[i] はあるか探索
3. ある時、あったよ
4. ない時、nums[i]は見たよと記録して次


初回の回答
```cpp
#include <unordered_map>
#include <vector>


class Solution {
public:
std::vector<int> twoSum(std::vector<int>& nums, int target) {
std::unordered_map<int, int> num_to_index;
for (int i = 0; i < nums.size(); ++i) {
int complement = target - nums[i];
if (num_to_index.count(complement)) {
return std::vector<int>{num_to_index[complement], i};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

initializer_list をコンストラクターにとる引数でインスタンス化して返すことができますので、

return {num_to_index[complement], i};

のほうがシンプルで良いと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@nodchip
コメントありがとうございます。

initializer_list をコンストラクターにとる引数でインスタンス化して返すことができますので、

把握できてなかったので、referenceをじっくり読んで次回書けるようにしたいと思います。
https://en.cppreference.com/w/cpp/language/initializer_list.html?utm_source=chatgpt.com
https://en.cppreference.com/w/cpp/utility/initializer_list.html?utm_source=chatgpt.com

}
num_to_index[nums[i]] = i;
}
return std::vector<int>{};
}
};

```


## Step2
- 初回同じキーに対してハッシュを2回使用しているので、find()という書き方も
- find rerturn に対しては、{}を返せる
- 「符号付きと符号なしの比較」あたりの修正。気づかなかった。

```cpp
#include <unordered_map>
#include <vector>


class Solution {
public:
std::vector<int> twoSum(std::vector<int>& nums, int target) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ここのnumsはconst参照にした方が良いかなと思います。

std::unordered_map<int, int> num_to_index;
for (int i = 0; i < static_cast<int>(nums.size()); ++i) {
int complement = target - nums[i];
auto it = num_to_index.find(complement);
if (it != num_to_index.end()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

C++20以降であれば、

num_to_index.contains(complement)

とした方が実際のコードの目的と合っているので読みやすいかと思います。

https://cpprefjp.github.io/reference/unordered_map/unordered_map/contains.html

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@ntanaka1984
コメントありがとうございます。
本当ですね。改めて確認しておきます。

return std::vector<int>{it->second, i};
}
num_to_index[nums[i]] = i;
}
return {};
}
};

```

- map&unordered_map復習(以下リンク)
- 選択の基準としては、要素の数・順序・セキュリティ・最悪計算量・小さいところでのメモリ使用量あたり
- https://github.com/ryosuketc/leetcode_grind75/pull/1/commits/2c5161514b4561a34193852a2a218603b35ca734#r2249075780
- https://chromium.googlesource.com/chromium/src/+/master/base/containers/README.md#Map-and-set-selection-Usage-advice
- https://stackoverflow.com/questions/3902644/choosing-between-stdmap-and-stdunordered-map

### 参考GitHub
- https://github.com/ryosuketc/leetcode_grind75/pull/1/commits/2c5161514b4561a34193852a2a218603b35ca734
- https://github.com/Ryotaro25/leetcode_first60/pull/12/commits/9c80068466e83724dc9004bca374e26f2b130b95


## Step3
6min
7min
5min
5min
5min