-
Notifications
You must be signed in to change notification settings - Fork 0
Solved Arai60/33. Search in Rotated Sorted Array #43
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?
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,102 @@ | ||
| ## Step 1. Initial Solution | ||
|
|
||
| - 始めは最小値が分かればそこからは二分探索するだけだと考えて最小値を二分探索→再度二分探索、という方針を考えた | ||
| - ただ、最小値が分かったところで結局分岐は沢山発生するのであまり意味がないことに気が付いた | ||
| - なるべくシンプルに考えたかったが、上手く条件をまとめられなかった | ||
| - left, middle, rightとtargetの大小関係を考えると l < m < r, m < r < l, r < l < mの3パターンにtargetがどこに入るかで×4通りを考えることになる | ||
| - よりシンプルに2つの値とtargetの位置関係を考えた | ||
| - これを関数において、left, middleとtargetの関係+middle, rightとtargetの関係が分かれば十分 | ||
| - nums[left] < nums[middle] < nums[right], nums[middle] < nums[right] < nums[left], nums[right] < nums[left] < nums[middle]の3パターンについて脳内でアルゴリズムを頑張って動かして合っていることを確認 | ||
| - 閉区間でやるとwhile文の中で長さ1のリストを処理しないので半開区間に修正して以下 | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def search(self, nums: List[int], target: int) -> int: | ||
| def is_between(left: int, right: int, target: int) -> int: | ||
| if nums[left] <= target <= nums[right-1]: | ||
| return True | ||
| if target <= nums[right-1] < nums[left]: | ||
| return True | ||
| if nums[right-1] < nums[left] <= target: | ||
| return True | ||
| return False | ||
|
|
||
| left = 0 | ||
| right = len(nums) | ||
| while left < right: | ||
| middle = (left + right) // 2 | ||
| if is_between(left, middle, target): | ||
| right = middle | ||
| continue | ||
| if is_between(middle, right, target): | ||
| left = middle | ||
| continue | ||
| return -1 | ||
| return left | ||
| ``` | ||
|
|
||
| ### Complexity Analysis | ||
|
|
||
| - 時間計算量:O(log n) | ||
| - 空間計算量:O(1) | ||
|
|
||
| ## Step 2. Alternatives | ||
|
|
||
| - 分岐が多くなってしまっても何も書かないよりは何か書けた方が良い | ||
| - コードとしては処理が多くて読みにくいが複雑な問題ではあるので初見でも理解はできる | ||
| - https://github.com/tokuhirat/LeetCode/pull/43/files#diff-681ab9d5fa98926ad078bf1aed23e33360ee5630c5c021079ce3f4fde79eb2d9R1 | ||
| - これとかを読んでも実際に手作業するときにやりそうな方法だと感じた | ||
| - https://github.com/olsen-blue/Arai60/pull/43/files#diff-0691fd54173ad5183bf1c632f87cc4a7353908565057fa6b9facae8e8ec5ec4bR16 | ||
| - 自分は大した技量も知識もないのに問題を綺麗に解こうとしすぎているのかもしれない | ||
| - 二分探索分かっているようで分かっていない感覚が強いがその正体がこの辺りにありそうだと感じた | ||
| - どのような書き方をするのかの選択肢が多いので一度止まって考えることが多いのも難しさの理由だと感じる | ||
| - 理解しやすい問題に置き換えて考えることの重要さが分かる | ||
| - https://github.com/Yoshiki-Iwasa/Arai60/pull/35#discussion_r1699552857 | ||
| - bisect_leftのkeyを上手く設定してやる方法があるらしい | ||
| - https://github.com/olsen-blue/Arai60/pull/43/files#diff-0691fd54173ad5183bf1c632f87cc4a7353908565057fa6b9facae8e8ec5ec4bR142 | ||
| - そもそも整序されていないリストをbisect_leftでやろうとするのが選択肢になかった | ||
| - 二つ条件を組み合わせれば確かに右肩上がりでtargetの点だけ違う値が出る関数を作ること自体は難しくはない | ||
| - 普通の整序リストなら二つ目の条件だけでfalse, false, false, true, true, …の最初のtrueを探せば良い | ||
| - 今回は同じ方法でやるとtrue, true, true, …false, false, true, true, …のようなパターンがあるのでそれに対応して手前のtrueをfalseにしてやらないといけない | ||
| - 頑張ってコードを理解するより使いたくなる気持ちを理解しておきたい | ||
| - 一つ目の条件の優先度を高くする必要はないことに気が付いた | ||
| - targetを越えないとスコアは2以上にならない | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def search(self, nums: List[int], target: int) -> int: | ||
| def score_position(num: int) -> int: | ||
| return (num <= nums[-1]) + (num >= target) | ||
|
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. (num <= nums[-1], num) で本当はいいみたいですね。 |
||
| index = bisect_left(nums, score_position(target), key=score_position) | ||
| if nums[index] == target: | ||
| return index | ||
| return -1 | ||
| ``` | ||
|
|
||
|
|
||
| ## Step 3. Final Solution | ||
|
|
||
| - こういう探索の仕方があるということを理解できたのは収穫だった | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def search(self, nums: List[int], target: int) -> int: | ||
| def score_num(num: int) -> int: | ||
| return -(num > nums[-1]) + (num >= target) | ||
|
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. ドキュメントにも書かれてました。初めて知りました。
|
||
|
|
||
| def binary_search_from_left(nums: list[int], target: int, key: Callable[[int], int]) -> int: | ||
| begin = 0 | ||
| end = len(nums) - 1 | ||
| while begin < end: | ||
| middle = (begin + end) // 2 | ||
| if key(nums[middle]) < key(target): | ||
| begin = middle + 1 | ||
| else: | ||
| end = middle | ||
| return begin | ||
|
|
||
| index = binary_search_from_left(nums, target, key=score_num) | ||
| if nums[index] == target: | ||
| return index | ||
| return -1 | ||
|
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. 面白い書き方だと思いました。 if target > nums[-1]:
if num >= target or num <= nums[-1]:
return 1
if target <= nums[-1]:
if num >= target and num <= nums[-1]:
return 1
return 0There 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. もしこうするならscore_numはis_past_targetみたいな名前のbooleanを返す関数にしてしまって、 |
||
| ``` | ||
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.
私の理解不足かもしれませんが、left = 0, middle = 0 となった時に is_between の right-1 は -1 になり、結果的に正しく動いているように見えて不思議に思いました。