Skip to content

349. Intersection of Two Arrays #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 22, 2024
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
32 changes: 32 additions & 0 deletions 4.hash-map/intersection-of-two-arrays/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## step1
- `map` でもいいが value を使わないので `std::set` か `std::unordered_set` を採用する。

- `std::set` と `std::unordered_set` のどちらを使う考える。
- `std::unordered_set` はハッシュテーブルなので整数徐算が遅いCPUアーキテクチャ (ARMなど) では遅くなる可能性がある。数回比較する (`std::set` を使う) 方が早い可能性がある。
- https://chromium.googlesource.com/chromium/src/+/master/base/containers/README.md#std_unordered_map-and-std_unordered_set
- `std::unordered_set`も C++11 からリハッシュできるようだが、最悪の場合で 計算量に`std::unordered_set::size` の二乗かかるので、(確率に依るが) あまり使いたくない。
- https://cpprefjp.github.io/reference/unordered_set/unordered_set/rehash.html
- `std::set` の方が良いと考える。

## step2
- 他のコードを見る

- 以下の初期化方法が C++17 からあった。
```cpp
std::unordered_set<int> unique_nums1(nums1.begin(), nums1.end());
```
- これなら `seen_in_nums1` より `unique_nums1` の方が自然。

- C++03 から `std::set_intersection` がある。
- `std::unordered_set` だと Wrong Answer になる。
- ソートしていない場合は未定義動作とある。(つまり何が起きても構わない。)
- https://en.cppreference.com/w/cpp/algorithm/set_intersection#:~:text=range%2C%20preserving%20order.-,1),-If
Copy link

Choose a reason for hiding this comment

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

set_intersection の実装は見ておいてください。
マージソート的な書き方はなんとなく頭にあります。
メモリーに乗らないくらい巨大でも、ソートされているならば、両方から取り出して、小さい方を進めていき、同じだったらそれを確保するということです。

誰か書いていたと思いますね。

Copy link
Owner Author

@Hurukawa2121 Hurukawa2121 Dec 22, 2024

Choose a reason for hiding this comment

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

ありがとうございます。

set_intersection の実装を見ました。
サイズが大きい方の配列の探索が対数時間で済むのですね。
こちらの議論@hroc135 さんが教えてくださいました。


- C++03 からの `std::back_inserter` をOutputIterator で使う。
- https://en.cppreference.com/w/cpp/iterator/back_inserter
- つまり `*(back_inserter(vec)) = value;` で `vec.push_back(value);` になる。
- 今回と関係ないが、他の書き込みを想定したイテレータを初めて知る。
- https://stackoverflow.com/questions/19907677/whats-the-difference-between-iterator-and-back-insert-iterator#:~:text=see%20the%20following-,(adaptor)%20iterators,-%3A

## step3
- 補完無しで2分弱ほどで実装する。
Copy link

Choose a reason for hiding this comment

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

このような議論もありました
katataku/leetcode#12 (comment)

Copy link
Owner Author

@Hurukawa2121 Hurukawa2121 Dec 21, 2024

Choose a reason for hiding this comment

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

共有していただきありがとうございます。

片方をソートする場合、引数をサイズが小さい配列と大きい配列に分け、小さい方をソートする方法を考えました。
(下で提案していただいた方法であれば、大きい配列を全探索する計算が一度で済みます。)

確かに、条件を変えたときに案があまり出てきませんでした。
そして、そもそも出題意図を推定する発想がありませんでした。
考えます。

23 changes: 23 additions & 0 deletions 4.hash-map/intersection-of-two-arrays/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <set>

class Solution {
public:
std::vector<int> intersection(std::vector<int>& nums1, std::vector<int>& nums2) {
std::set<int> seen_in_nums1;
std::set<int> seen_in_nums2;

Choose a reason for hiding this comment

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

この変数はseen_in_num2よりはaddedみたいな名前のほうが挙動と名前の距離が近くてわかりやすくなりそうと思いました。
更に付け加えると宣言場所と利用場所が遠いので、近づけるとこの変数は今から使うということを明示できるので読みやすくなります。

Copy link
Owner Author

@Hurukawa2121 Hurukawa2121 Dec 21, 2024

Choose a reason for hiding this comment

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

ありがとうございます。
added とはどのような挙動を表現したものでしょうか。
宣言場所、非常に納得しました。
次から近づけるようにします。

Choose a reason for hiding this comment

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

added とはどのような挙動を表現したものでしょうか

「最後にreturnするvectorへ追加済であること」の追加済を意味するaddedです。

Copy link
Owner Author

@Hurukawa2121 Hurukawa2121 Dec 22, 2024

Choose a reason for hiding this comment

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

added_to_intersected_nums のような命名と解釈しました。
確かに、 その方がカード条件の意味が明確です。
お答えいただきありがとうございます。

for (int num : nums1) {
seen_in_nums1.insert(num);
Copy link

Choose a reason for hiding this comment

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

これ、コンストラクタでできませんでしたっけ。.begin(), .end() を渡すと。

Copy link

Choose a reason for hiding this comment

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

あ、下にありましたね。

}
std::vector<int> intersected_nums;
for (int num : nums2) {
if (seen_in_nums2.contains(num)) {
continue;
}
if (seen_in_nums1.contains(num)) {
intersected_nums.push_back(num);
seen_in_nums2.insert(num);
}
}
return intersected_nums;
}
};
Copy link

Choose a reason for hiding this comment

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

  1. seen_in_nums1 を作る
  2. nums2を走査し、seen_in_nums1に含まれるものがあればintersected_numsに追加し、seen_in_nums1から削除する
    というアルゴリズムも考えられます

Copy link
Owner Author

@Hurukawa2121 Hurukawa2121 Dec 21, 2024

Choose a reason for hiding this comment

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

ありがとうございます。
そのとき seen_in_nums1 が名前から想起される役割をしなくなってしまいました。
そして、代わりの変数名が思いつかなかったため諦めました。
(メモに書くべきでしたすいません。。)

何か良い変数名はありますでしょうか。

Copy link

Choose a reason for hiding this comment

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

nums1_not_in_nums2 しか思いつかないですが、なんか不恰好ですね、、

Copy link
Owner Author

Choose a reason for hiding this comment

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

not_inの発想はなかったです。
ありがとうございます。

12 changes: 12 additions & 0 deletions 4.hash-map/intersection-of-two-arrays/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <set>

class Solution {
public:
std::vector<int> intersection(std::vector<int>& nums1, std::vector<int>& nums2) {
std::set<int> unique_nums1(nums1.begin(), nums1.end());
std::set<int> unique_nums2(nums2.begin(), nums2.end());
std::vector<int> intersected_nums;
std::set_intersection(unique_nums1.begin(), unique_nums1.end(), unique_nums2.begin(), unique_nums2.end(), back_inserter(intersected_nums));
return intersected_nums;
}
};
12 changes: 12 additions & 0 deletions 4.hash-map/intersection-of-two-arrays/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <set>

class Solution {
public:
std::vector<int> intersection(std::vector<int>& nums1, std::vector<int>& nums2) {
std::set<int> unique_nums1 = std::set(nums1.begin(), nums1.end());
std::set<int> unique_nums2 = std::set(nums2.begin(), nums2.end());
std::vector<int> intersected_nums;
std::set_intersection(unique_nums1.begin(), unique_nums1.end(), unique_nums2.begin(), unique_nums2.end(), std::back_inserter(intersected_nums));
return intersected_nums;
}
};