From 97a426ff1e9e6c725bdf9851310e4dd34fdededa Mon Sep 17 00:00:00 2001 From: Taito Ohsumi Date: Tue, 24 Dec 2024 13:31:28 +1100 Subject: [PATCH 1/2] Add 349. Intersection of Two Arrays.md --- 349. Intersection of Two Arrays.md | 122 +++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 349. Intersection of Two Arrays.md diff --git a/349. Intersection of Two Arrays.md b/349. Intersection of Two Arrays.md new file mode 100644 index 0000000..4b0a12c --- /dev/null +++ b/349. Intersection of Two Arrays.md @@ -0,0 +1,122 @@ +# step 1 +考えたこと +- nums1, num2で二重ループ作って、値比較すれば解ける +- num1, num2のどちらかをhashtableに入れればループが一つ減る + - 値->存在するか否かのboolがほしい + - ならsetでいいか +- unique elementだけのリストを返さないといけない +- 一つだけsetを取ると、返り値を保存しているlistのuniqueチェックをどこかでしないといけない +- 両方setをとってintersectionするのが穏当そう + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + set1 = set(nums1) + set2 = set(nums2) + return list(set1 & set2) +``` + +n, mをnums1.length, num2.lengthとして +- time complexity: O(m + n) +- space complexity: O(m + n) + +# step 2 +- 一応,`element in list`の挙動を確認した + - https://github.com/python/cpython/blob/30efede33ca1fe32debbae93cc40b0e7e0b133b3/Objects/listobject.c#L619-L636 + - 特別なことはなく、端から端まで調べているで良さそう +- set intersectionの挙動 + - https://github.com/python/cpython/blob/39e69a7cd54d44c9061db89bb15c460d30fba7a6/Objects/setobject.c#L1354 + - [要素数の小さいsetをiterateする様にしていた](https://github.com/python/cpython/blob/39e69a7cd54d44c9061db89bb15c460d30fba7a6/Objects/setobject.c#L1372-L1376)ので、両方setでintersectionを取るときはこの操作自体はO(min(m, n)) +- https://github.com/katataku/leetcode/pull/12/files + - 「setに変換したりlistに変換したりするのって意外と抵抗がある」 + - 変換にはすべての要素を走査するコストがあるので、その点を理解する必要がある。 + - https://github.com/nittoco/leetcode/pull/15#discussion_r1632066961 + - 追加の問題として、片方が非常に大きくてもう片方が非常に小さい場合、特に大きいほうがsortされている場合 + - 両方sortされている場合 +- https://github.com/tarinaihitori/leetcode/pull/13 + - step1の様な書き方をして、わざわざsetしたものを変数でおく必要はない。 + - setを一つだけ使う(というより、space complexityをO(min(m, n))としたい場合の)解法 + - これも片方がものすごく大きい場合どうするかに相当するとおもった。 +- https://github.com/hroc135/leetcode/pull/13/files + - pythonでも同様だが、[変数名に型の情報を不必要に入れるべきでない](https://google.github.io/styleguide/pyguide.html#2144-decision:~:text=names%20that%20needlessly%20include%20the%20type%20of%20the%20variable%20(for%20example%3A%20id_to_name_dict))というスタイルがある + - 今回はintersectionだけだと関数名と同じになるし、intersectionで想定するのが集合な気がするが、返り値はintersectionを保持するリストであるので、型名を入れることは許容範囲に感じた +- こういう簡単な問題だと特に、問題を見たときに想定する範囲や取り出す知識の幅がまだまだ小さい様に感じた。 + +片方が非常に大きくてもう片方が非常に小さい場合 +m = large_nums.length, n = small_nums.lengthとして、 +- time complexity: O(m + n) +- space complexity: O(n) +`small_nums_set.remove(num)`をなくして、ループないで`if num in intersection_list`とすることもできる。が、この場合、intesection_listを全て調べることになる。 +```python +class Solution: + def intersection(self, large_nums: List[int], small_nums: List[int]) -> List[int]: + small_nums_set = set(small_nums) + intersection_list = [] + for num in large_nums: + if num in small_nums_set: + small_nums_set.remove(num) + intersection_list.append(num) + return intersection_list +``` + +片方が非常に大きくてもう片方が非常に小さい場合、特に大きいほうがsortされている想定 +m = large_nums.length, n = small_nums.length、sort処理を無視するとして、 +- time complexity: O(n log m) +- space complexity: O(min(m, n)) = O(n) +```python +class Solution: + def intersection(self, large_nums: List[int], small_nums: List[int]) -> List[int]: + def is_member(sorted_nums: List[int], element: int) -> bool: + left = 0 + right = len(sorted_nums) + while left < right: + mid = (left + right) // 2 + if sorted_nums[mid] == element: + return True + elif sorted_nums[mid] < element: + left = mid + 1 + else: + right = mid + return False + sorted_large_nums = sorted(large_nums) + intersection_list = [] + for num in small_nums: + if num in intersection_list: + continue + if is_member(sorted_large_nums, num): + intersection_list.append(num) + return intersection_list +``` + +両方sortされている想定 + +sort処理を無視すれば、 +- time complexity: O(m + n) +- space complexity: O(min(m, n)) +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + sorted_nums1 = sorted(nums1) + sorted_nums2 = sorted(nums2) + index1 = 0 + index2 = 0 + intersection_set = set() + while index1 < len(sorted_nums1) and index2 < len(sorted_nums2): + if sorted_nums1[index1] == sorted_nums2[index2]: + intersection_set.add(sorted_nums1[index1]) + index1 += 1 + index2 += 1 + elif sorted_nums1[index1] < sorted_nums2[index2]: + index1 += 1 + else: + index2 += 1 + return list(intersection_set) +``` + +# step 3 + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + return list(set(nums1) & set(nums2)) +``` \ No newline at end of file From 44c90302ee7896460687db15263de5bafa1f4a03 Mon Sep 17 00:00:00 2001 From: Taito Ohsumi Date: Sat, 1 Feb 2025 23:04:35 +1100 Subject: [PATCH 2/2] Add step4 --- 349. Intersection of Two Arrays.md | 101 +++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/349. Intersection of Two Arrays.md b/349. Intersection of Two Arrays.md index 4b0a12c..738972d 100644 --- a/349. Intersection of Two Arrays.md +++ b/349. Intersection of Two Arrays.md @@ -119,4 +119,105 @@ class Solution: class Solution: def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: return list(set(nums1) & set(nums2)) +``` + +# step 4 +コメントまとめ +- 略語は避けるが慣習として明らかならよい +- 同じ値がきたら、違う値になるまでindexを進める方法もある + - 人に説明する時にこっちの方が自然な形のように感じた。 + +- time complexity: O(m + n) +- space complexity: O(m + n) +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + return list(set(nums1) & set(nums2)) +``` + + +想定 +- どちらも昇順にsortされている + - indexを用意して動かす + - time complexity: O(max(m, n)) +- 片方が大きく、片方が小さい(mを大きい方の要素数、nを小さい方とする) + - sortとかはされてない + - 片方をsetに変換する。もう片方をiterate + - time complexity: O(m + n) = O(n) + - space complexity: O(n) + - 大きい方がsortされている + - quick select: + - time complexity: O(n log m) + - space complexity: O(n) + - 小さい方がsortされている + - sortされてない場合と同じように解くとtime complexityは小さくなる + +昇順sort済み +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + sorted_nums1 = sorted(nums1) + sorted_nums2 = sorted(nums2) + result = [] + index1 = 0 + index2 = 0 + while index1 < len(sorted_nums1) and index2 < len(sorted_nums2): + if sorted_nums1[index1] < sorted_nums2[index2]: + index1 += 1 + continue + if sorted_nums1[index1] > sorted_nums2[index2]: + index2 += 1 + continue + + duplicated_value = sorted_nums1[index1] + result.append(duplicated_value) + while ( + index1 < len(sorted_nums1) + and sorted_nums1[index1] == duplicated_value + ): + index1 += 1 + while ( + index2 < len(sorted_nums2) + and sorted_nums2[index2] == duplicated_value + ): + index2 += 1 + return result +``` + +```python +class Solution: + def intersection(self, small_nums: List[int], large_nums: List[int]) -> List[int]: + small_nums_set = set(small_nums) + result = [] + for number in large_nums: + if number in small_nums_set: + result.append(number) + small_nums_set.remove(number) + return result +``` + +```python +class Solution: + def intersection(self, small_nums: List[int], large_nums: List[int]) -> List[int]: + def is_member(number: int, sorted_nums: List[int]) -> bool: + left = 0 + right = len(sorted_nums) + while left < right: + mid = (left + right) // 2 + if sorted_nums[mid] == number: + return True + if sorted_nums[mid] < number: + left = mid + 1 + else: + right = mid + return False + + sorted_large_nums = sorted(large_nums) + result = [] + for number in small_nums: + if number in result: + continue + if is_member(number, sorted_large_nums): + result.append(number) + return result ``` \ No newline at end of file