Skip to content
Open
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
89 changes: 89 additions & 0 deletions 0349-intersection-of-two-arrays/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# 349. Intersection of Two Arrays

- ふたつのint配列が与えられる。積集合のリストを返せ。順番の制限はない。
- 1 <= nums1.length, nums2.length <= 1000
- 0 <= nums1[i], nums2[i] <= 1000


## Step 1
### 解法1
- ふたつの配列をsetにしてループで回せばすぐ終わりそう。
- 時間計算量は、nums1の走査でO(n)、nums2へのin判定はsetがハッシュ化されていることからmにつきO(1)で、O(n + m)。
- 空間計算量はセットやリストの確保分でO(n + m)。
```py
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1_set = set(nums1)
nums2_set = set(nums2)

res = []
for num in nums1_set:
if num in nums2_set:
res.append(num)

return res
```
- Leetcodeのruntimeは0msになっていて、ほんとうにこれ信用できない代物だなあという感を新たにする。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LeetCode の実行時間、確かにブレが激しいですよね。
試しに手で計算してみるといいかもしれません。
Yuto729/leetcode#16 (comment)
kazizi55/coding-challenges#10 (comment)


### 解法2
- nums1を走査して出てきたものを辞書に{文字: 1}として登録。
- nums2を走査して、バリューが1の文字を結果にアペンド。同時にインクリメントして被りを防ぐ。
- 解法1に比べると、空間計算量がO(n)になる点が優れている。
```py
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
num_to_seen = {}
res = []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

num_to_frequency に変えて frequency が2以上の num の配列を返すようにすれば res なしでも解けると思います。


for num in nums1:
num_to_seen[num] = 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

num_to_seen と聞くと、すでにみた数字かどうかという bool が value に入るように感じるので、個人的にはnum_to_seen[num] = Trueにするのが自然に思えました。
また、実際は nums1 の頻度を格納しているので、num1_to_seenとする方が親切かもしれません。
今のまま頻度を格納したいなら、num_to_frequency とする方が実態に即しているかもです。


for num in nums2:
if num_to_seen.get(num) == 1:
res.append(num)
num_to_seen[num] += 1

return res
```


## Step2
### AI
- `set1 & set2`か、`set1.intersection(set2)`で積集合が求められる。
- `return list(set(nums1) & set(nums2))`で終わる。
- `&`は初めて知った。`intersection()`は前勉強したのに忘れていたので反省。
- `a | b`か`a.union(b)`で和集合、`a - b`か`a.difference(b)`で差集合、`a ^ b`か`a.symmetric_difference(b)`で対称差(どちらか一方にだけあるもの)。
- 包含関係も表せる。`a <= b`か`a.issubset(b)`で部分集合、`a >= b`か`a.issuperset(b)`で上位集合、`a.isdisjoint(b)`で共通要素の判定。(https://docs.python.org/3/library/stdtypes.html#set.issuperset)

### コメント集
- https://github.com/quinn-sasha/leetcode/pull/13#discussion_r1960884543
> この問題は問題文自体では終わっていなくて、解けた後に、いくつか追加の条件が出てきて、その下でのアルゴリズムとそれらの pros and cons が要求されると思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

もうご覧になっているかもしれませんが、binary search を使う解法もあります。
katataku/leetcode#12 (comment)

- たしかにこの問題は(Pythonだと)瞬殺なので、プラスアルファの学びにしたい。

- たとえば入力がソート済みなら、TwoPointersによるマージソートみたいなことをすると空間計算量がO(1)で済み(時間はO(n + m)のまま)、集合を作るやり方より優れている。
```py
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
# 入力がソート済みと仮定したいのでここでソート
nums1.sort()
nums2.sort()

i = 0
j = 0
res = []
while i < len(nums1) and j < len(nums2):
if nums1[i] == nums2[j]:
if not res or res[-1] != nums1[i]:
res.append(nums1[i])
i += 1
j += 1
elif nums1[i] < nums2[j]:
i += 1
elif nums1[i] > nums2[j]:
j += 1

return res
```

## Step3
`return list(set(nums1) & set(nums2))`は三回書くまでもないかと思い、上記Two Pointersのやり方で三回再現。
27 changes: 27 additions & 0 deletions 0349-intersection-of-two-arrays/step1.py

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

*.py の内容が memo.md と重複しているので手元で実行したログを残しておきたいなどの理由がない限りは memo.md に統一しても良いのではと思いました。

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1_set = set(nums1)
nums2_set = set(nums2)

res = []
for num in nums1_set:
if num in nums2_set:
res.append(num)

return res


class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
num_to_seen = {}
res = []

for num in nums1:
num_to_seen[num] = 1

for num in nums2:
if num_to_seen.get(num) = 1:
res.append(num)
num_to_seen[num] += 1

return res
21 changes: 21 additions & 0 deletions 0349-intersection-of-two-arrays/step2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
# 入力がソート済みと仮定したいのでここでソート
nums1.sort()
nums2.sort()

i = 0
j = 0
res = []
while i < len(nums1) and j < len(nums2):
if nums1[i] == nums2[j]:
if not res or res[-1] != nums1[i]:
res.append(nums1[i])
i += 1
j += 1
elif nums1[i] < nums2[j]:
i += 1
elif nums1[i] > nums2[j]:
j += 1

return res