-
Notifications
You must be signed in to change notification settings - Fork 0
Two Sum #12
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
base: main
Are you sure you want to change the base?
Two Sum #12
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# Two Sum | ||
|
||
## Step1 | ||
|
||
### 考えたこと | ||
|
||
全部の数字の和を要素として持っといてそれと一致するものを出力すれば良いのでは | ||
j は i < j <= len(nums) にしないと同じ和を2回計算することになる | ||
i は len(nums) - 1 にしないと i = len(nums)で j が定義できなくなる | ||
|
||
```python | ||
class Solution: | ||
def twoSum(self, nums: List[int], target: int) -> List[int]: | ||
sum_to_indices = {} | ||
|
||
for i in range(len(nums) - 1): | ||
for j in range(i + 1,len(nums)): | ||
sum_nums = nums[i] + nums[j] | ||
sum_to_indices[sum_nums] = [i,j] | ||
|
||
return sum_to_indices[target] | ||
``` | ||
|
||
## Step2 | ||
|
||
### Hash Mapとは | ||
|
||
ハッシュ関数を用いてキーを一意に管理し、そのキーに対応するvalueを保持する。pythonではdictがそれ | ||
- 方法 | ||
- キーからハッシュ値を計算する | ||
- ハッシュ値を元にバケット(Hash Map内の配列)のどこに格納するかを決める | ||
- 値を格納・取得する | ||
- 格納:バケットにキーとバリューのペアを保存 | ||
- 取得:キーからハッシュ値を計算し、対応するバケットからバリューを取り出す | ||
- 特徴 | ||
- 速い:検索・追加・削除がO(1) | ||
- 衝突 | ||
- 違うキーで同じハッシュ値が計算されてしまうこと | ||
- 対処法 | ||
- チェイニング:同じハッシュ値の要素をまとめて保存する(連結リストなどを使用) | ||
- オープンアドレッシング:空いてるバケットを探して再確認する | ||
- pythonのdictはこれを用いている(二次探査) | ||
- リハッシュ:バケットが要素で満たされてくると自動的にバケットのサイズを増やして再配置を行う | ||
- 衝突を軽減する | ||
- リハッシュの際は一時的にO(n)かかる | ||
|
||
### 参考にしたもの | ||
|
||
https://github.com/katsukii/leetcode/pull/2/files/7023788fe5ed525606712b0c5f4fd025d5f03f21#diff-e834754f59f018414ec6917d9f04e6be9d730c536df3f52d9181d7aa3539b651 | ||
|
||
java読んだことなくて難しい。 | ||
書かれているものをpythonで書くと以下のようになる | ||
```python | ||
class Solution: | ||
def twoSum(self, nums: List[int], target: int) -> List[int]: | ||
num_to_index = {} | ||
for i in range(len(nums)): | ||
complement = target - nums[i] | ||
if complement in num_to_index: | ||
return [num_to_index[complement], i] | ||
num_to_index[nums[i]] = i | ||
|
||
raise ValueError("No two sum pair found.") | ||
``` | ||
|
||
targetから引いた余りがnum_to_indexの中にないか探す | ||
numsの中から探せばいいじゃんと思ったが、numsは配列なのでもう一周しないと見つからない | ||
- 計算量 | ||
- 時間計算量 | ||
- O(n) | ||
- 空間計算量 | ||
- 最悪O(n) | ||
|
||
https://github.com/Hurukawa2121/leetcode/pull/11 | ||
|
||
C++も読んだことない | ||
解法は同じ | ||
赤黒木 std::mapというものも使えるらしい | ||
|
||
### 赤黒木 | ||
|
||
自己平衡二分探索木の一種 | ||
- 性質 | ||
- 各ノードが赤もしくは黒である | ||
- ルートノードは常に黒 | ||
- 全ての葉ノードは黒 | ||
- 赤ノードの子は黒 | ||
- 任意のノードからその子孫の葉ノードに至るまでの黒ノードの数は同じ | ||
- 利点 | ||
- 挿入、削除、検索がO(logn) | ||
|
||
https://github.com/t0hsumi/leetcode/pull/11 | ||
|
||
入力をソートして両端から調べていく方法もある | ||
left、rightが行きすぎて戻ることがあるからHashmapのものより少しだけ遅そうに見える | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. いろんなパターンの実装をされていて素晴らしいと思いました。
僕の理解のために教えて欲しいのですが、「行きすぎる」ってどういうパターンでしょうか。
に見えて、あれ?っとなりました。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。よく考えたら行き過ぎることはなかったです。 [a, b, c, d, e]とかで target = b + d の時 a + e < target < c + e だと c まで行きすぎてから戻るのかなとなんとなく思ってたのですが、 b + e > b + d が必ず成立するのでそんなことにはならなかったです。 |
||
- 計算量 n = len(nums) | ||
- 時間計算量 | ||
- ソート O(nlogn) | ||
- 左右からの探索 O(n) | ||
- 空間計算量 | ||
- num_index_pairs O(n) | ||
|
||
```python | ||
class Solution: | ||
def twoSum(self, nums: List[int], target: int) -> List[int]: | ||
num_index_pairs = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. パフォーマンスの違いはほとんどないと思いますが、sorted 関数の key 引数を利用すれば、リストを新たに生成せずとも処理が可能です
|
||
for i, num in enumerate(nums): | ||
num_index_pairs.append((num, i)) | ||
num_index_pairs.sort() | ||
|
||
left = 0 | ||
right = len(nums) - 1 | ||
while left < right: | ||
two_sum = num_index_pairs[left][0] + num_index_pairs[right][0] | ||
if two_sum == target: | ||
return [num_index_pairs[left][1], num_index_pairs[right][1]] | ||
|
||
if two_sum > target: | ||
right -= 1 | ||
else: | ||
left += 1 | ||
|
||
raise RuntimeError( | ||
f"No pair found in twoSum(): nums = {nums}, target = {target}" | ||
) | ||
``` | ||
|
||
for i in range(len(nums))と for i, _ in enumerate(nums) はどっちが早いんだろう | ||
https://stackoverflow.com/questions/11990105/rangelenlist-or-enumeratelist | ||
rangeの方がちょい早いらしい?enumerateはtupleを出力してるからか。 | ||
rangeの方が絶対にわかりやすくはある。 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Python がそもそもあんまり速くないので、このあたりの速度はあまり気にしなくていいでしょう。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. より正確に書くと、計算量を求め、データサイズを代入し、おおよその計算ステップ数を求め、言語ごとの処理速度を考慮し、許容される実行時間内に実行可能な計算ステップ数以内に十分収まっていれば、ひとまず大丈夫だと思います。速度を求めるなら、言語を変えることも一つの手だと思います。 |
||
|
||
## Step3 | ||
|
||
ハッシュマップで書いた | ||
|
||
```python | ||
class Solution: | ||
def twoSum(self, nums: List[int], target: int) -> List[int]: | ||
num_to_index = {} | ||
for i in range(len(nums)): | ||
complement = target - nums[i] | ||
if complement in num_to_index: | ||
return [num_to_index[complement], i] | ||
num_to_index[nums[i]] = i | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C++ の企画上は、 std::map を赤黒木で実装しなければならないとは書いておらず、計算量のみが規定されています。ただし、赤黒木で実装されることが多いようです。
https://timsong-cpp.github.io/cppwp/n4950/map.access
https://en.cppreference.com/w/cpp/container/map