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
140 changes: 140 additions & 0 deletions 349/349. Intersection of Two Arrays.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# [349. Intersection of Two Arrays](https://leetcode.com/problems/intersection-of-two-arrays/)
## Step1
### 問題意図の考察
- 問題文の確認
1. 2つの整数配列nums1, nums2
2. 両方の配列に共通して存在する要素のみを返して
3. 結果の各要素は、重複なしであること
4. 返す順番は任意で良い
Comment on lines +5 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: space4個分でインデントなので,もう一つtabを入れるかスペース2つを入れるかしないとインデントにならないですね(自分の環境でもtab1回でインデントできる時とtab2回必要な時があって困惑しています.)


- 制約の確認
1. 1 <= nums1.length, nums2.length <= 1000
2. 0 <= nums1[i], nums2[i] <= 1000
-> 1000 * 1000 最悪O(10^6) 100万
-> unordered_set: O(n), ソート: O(n log n)

### 解法を考える。
- ハッシュセット?ソート?どっちがいいかな
- ソート O(n log n)/ ハッシュセット O(n)かな。
- 問題を解いた後に、色々条件が追加される。その中で何がpros and consが挙げられるようにしたい
- 解法としては、nums1の要素を繰り返しハッシュセットに入れていく(重複しない)
- nums2も繰り返し入れていって、同じkeyを要素で返す

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

class Solution {
public:
std::vector<int> intersection(std::vector<int>& nums1, std::vector<int>& nums2) {
std::unordered_set<int> nums1_set;
for (int i = 0; i < nums1.size(); ++i) {
nums1_set.insert(nums1[i]);
}
Comment on lines +31 to +34
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_set<int> nums1_set(nums.begin(), nums2.end());


std::unordered_set<int> result_set;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

結局,vectorに対するpush_back()も償却計算時間はO(1)なので,ここをハッシュマップにするモチベーションがあるのか気になりました.
もちろん係数部分がどちらが大きいか次第ではありますが,そもそもこの箇所がコード全体のボトルネックになるとは考えにくいとも思います.

for (int i = 0; i < nums2.size(); ++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.

ranaged for 文で書いたほうがシンプルに書けると思います。

for (int i : nums2) {
    if (nums1_set.find(i) != nums1_set.end()) {
        result_set.insert(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
コメントありがとうございます。
そうですよね。ranged for文の書き方に慣れておらず避けてしまいました。
たくさん書いて練習します。
https://en.cppreference.com/w/cpp/language/range-for.html?utm_source

if (nums1_set.find(nums2[i]) != nums1_set.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.

std::set::contains のほうがシンプルに書けると思います。

if (nums1_set.contains(nums2[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
ありがとうございます。
メンバ関数を知らな過ぎますね、もっと知識増やします。
https://en.cppreference.com/w/cpp/container/unordered_set/contains.html?utm_source

result_set.insert(nums2[i]);
}
}

std::vector<int> result;
for (std::unordered_set<int>::iterator it = result_set.begin(); it != result_set.end(); ++it) {
result.push_back(*it);
}
return result;
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::vector なので、 std::vector のコンストラクターを呼び出す書き方で、シンプルに書けると思います。

return {result_set.begin(), result_set.end()};

ただ、この書き方はあまり見ないかもしれません。

}
};

```

## Step2
- 改善点
1. loop内の整理
2. for文+set+vectorのみのシンプルな構造
3. 命名 result_set -> intersection_setへ nums2_setだと誤解が生まれる可能性がある

- step1の修正
```cpp
#include <vector>
#include <unordered_set>

class Solution {
public:
std::vector<int> intersection(std::vector<int>& nums1, std::vector<int>& nums2) {
std::unordered_set<int> nums1_set;
for (int i = 0; i < nums1.size(); ++i) {
nums1_set.insert(nums1[i]);
}

std::unordered_set<int> intersection_set;
for (int i = 0; i < nums2.size(); ++i) {
if (nums1_set.find(nums2[i]) != nums1_set.end()) {
intersection_set.insert(nums2[i]);
}
}

std::vector<int> result(intersection_set.begin(), intersection_set.end());
return result;
}
};

```

- 他の方のコードを読む
* https://github.com/5ky7/arai60/pull/14/commits/5d8750fdca0c2c1e2bcc48a19be824450f931700

```cpp
set<int> set_nums1(num1.begin(), num1.end());
```

と書いた方がシンプル

* https://github.com/5103246/LeetCode_Arai60/pull/13/commits/012aa0d6ed21bdb52c6dd416e2a8d8d76c4407ea
-> setとunordered_setここでは、そんなに誤差はないが、メモリオーバーヘッドが大きい、実装によってはcacheに乗りにくくて遅くなることもある。
-> 解法の段階で思い込み。解法の時に手札を増やすこと。

- 全体的な修正(set)

```cpp
#include <set>
#include <vector>

class Solution {
public:
std::vector<int> intersection(std::vector<int>& nums1, std::vector<int>& nums2) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nums1, nums2は内部での変更がないため引数は非const参照にしてもよいかと思います。

std::set<int> nums1_set(nums1.begin(), nums1.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.

変数名にコンテナ名を入れても、読み手にとってあまり有益ではないかもしれません。
unique_nums1とかはどうでしょうか?

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.

@5103246
ご指摘ありがとうございます。
これは良くないですね。unique_nums1参考にさせて頂きます!


std::set<int> intersection_set;
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::set_intersectionがあります。
https://cpprefjp.github.io/reference/algorithm/set_intersection.html
nums1, nums2をsetに変換すると本問題で使える形になります。

for (int i = 0; i < nums2.size(); ++i) {
if (nums1_set.count(nums2[i])) {
intersection_set.insert(nums2[i]);
}
}

std::vector<int> result(intersection_set.begin(), intersection_set.end());
return result;
}
};

```

- https://google.github.io/styleguide/cppguide.html#Performance
- https://en.cppreference.com/w/cpp/iterator.html
-> イテレータの振る舞い。挙動がよく分からなかった。もう一度確認必要。
- https://en.cppreference.com/w/cpp/container/vector.html
- https://en.cppreference.com/w/cpp/container/set.html
- https://en.cppreference.com/w/cpp/container/unordered_set.html
-> vector/set/unordered_setの確認

- 他の解法 (ソート:時間がないので次回)

## Step3 *自然な流れ(感覚に落とし込む)
1. 4min
2. 4min
3. 5min
4. 2min
5. 1min