Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# 33. Search in Rotated Sorted Array
## STEP1
- 何も見ずに解いてみる
- [left, right] を候補とする。left = right の時 middle = left となり、target と比較される。等しくない場合には次の while 文は実行されず −1 が返る。範囲の狭め方としては、rotate 位置と middle と target の位置関係で場合分けした。
```python
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
middle = (left + right) // 2
if nums[middle] == target:
return middle
if nums[middle] < target:
if nums[middle] >= nums[left]:
left = middle + 1
continue
if target <= nums[right]:
left = middle + 1
else:
right = middle - 1
else:
if nums[middle] <= nums[right]:
right = middle - 1
continue
if target >= nums[left]:
right = middle - 1
else:
left = middle + 1
return -1
```

## STEP2
### プルリクやドキュメントを参照
- https://github.com/olsen-blue/Arai60/pull/43/files
- middle と right が指す数の大小関係を先に判定している。崖を超える前後がわかり、場合分けがシンプルになる。
- https://discord.com/channels/1084280443945353267/1233295449985650688/1239594872697262121
- https://discord.com/channels/1084280443945353267/1233295449985650688/1239446770761596928
- 最初解こうとした時に、nums[middle] と target と nums[-1] の大小関係でどうにかしようと思って失敗していた。*2 をする発想がなかったので、崖を越えたのか、middle を越えたのか見分けがつけられなかった。なるほど、このようにするとできる方法があるのかと驚いた。
- https://github.com/Yoshiki-Iwasa/Arai60/pull/36#discussion_r1712955053
ここまで整理できていたらわかりやすいですね。ただ思いつきにくいなとも思う。

場合分けを整理して、2分探索の確認のため区間の取り方を変えて書いてみる。[left, right) に解の候補を含まれるように探索。
```python
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)
while left < right:
middle = (left + right) // 2
if nums[middle] == target:
return middle
if nums[middle] > nums[-1]:
if nums[0] <= target < nums[middle]:
right = middle
else:
left = middle + 1
else:
if nums[middle] < target <= nums[-1]:
left = middle + 1
else:
right = middle
return -1
```
- https://github.com/tokuhirat/LeetCode/pull/41/files#r2217335504
- 探す数字が配列に含まれていない場合に -1 を返す場合、index の候補が一つになってもそれが探している値を指すか確認しないといけない(対照的なケースとして挿入箇所を探す場合)。これをwhile文の中でやると終了時は解の候補がなくなるため left > right になるのは当たり前だなとわかるようになった。ただ、挿入箇所を探す場合には、最後に候補indexが一つ残るようにする(解釈する)方が良さそうかなと思っています。

## STEP3
### 3回ミスなく書く
個人的には閉区間で書いた方がわかりやすいです。
```python
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
middle = (left + right) // 2
if nums[middle] == target:
return middle
if nums[middle] > nums[-1]:
if nums[0] <= target < nums[middle]:
right = middle - 1
else:
left = middle + 1
else:
if nums[middle] < target <= nums[-1]:
left = middle + 1
else:
right = middle - 1
return -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.

いいと思います。
が、個人的には回転位置を見つけたのちにソート済み配列(先頭から回転位置 or 回転位置から末尾)を探索する方針の方が理解しやすいかと思います

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

コメントありがとうございます。書いてみました。わかりやすい方針だと思いました。

import bisect


class Solution:
    def search(self, nums: List[int], target: int) -> int:
        min_index = self._find_min_index(nums)
        if target > nums[-1]:
            i = bisect.bisect_left(nums[:min_index], target)
            if i < min_index and nums[i] == target:
                return i
        else:
            i = bisect.bisect_left(nums[min_index:], target) + min_index
            if i < len(nums) and nums[i] == target:
                return i
        return -1
        
    def _find_min_index(self, nums: List[int]) -> int:
        return bisect.bisect_left(nums, True, key=lambda x: x <= nums[-1])


3分,2分,2分で3回Accept