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
132 changes: 132 additions & 0 deletions Python3/35. Search Insert Position.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
## Step 1. Initial Solution

- bisect.bisect_leftで答えは得られる
- 自分でこれを実装する
- begin, endの定義は他にもやりようがある
- ここでは[begin, end)で定義している
- begin-1より大きく、endより小さい位置にある
- begin == endになったらその位置に入れてあげれば良い

```python
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
def binary_search(begin: int, end: int) -> int:
Copy link

Choose a reason for hiding this comment

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

関数名には、どのような値が返ってくるかを表す名前を付けたほうが、ソースコードが理解しやすくなると思います。

middle = (begin + end) // 2
if begin == end or target == nums[middle]:
return middle
if target < nums[middle]:
return binary_search(begin, middle)
if target > nums[middle]:
return binary_search(middle + 1, end)
return binary_search(0, len(nums))
```

### Complexity Analysis

- 時間計算量:O(log n)
- 空間計算量:O(1)

## Step 2. Alternatives

- 他にも書き方を試してみる
- target == nums[middle]をなくす

```python
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
def binary_search(begin: int, end: int) -> int:
middle = (begin + end) // 2
if begin == end:
return middle
if target <= nums[middle]:
return binary_search(begin, middle)
else:
return binary_search(middle + 1, end)
return binary_search(0, len(nums))
```

- iterationで解く

```python
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
begin = 0
end = len(nums)
while begin < end:
middle = (begin + end) // 2
if target <= nums[middle]:
end = middle
else:
begin = middle + 1
return begin
```

- 確かにleft, rightの方が良く見かけるのを思い出した
- https://github.com/tokuhirat/LeetCode/pull/41/files#diff-2b5524d310a8ba9fe7670e916be44468c8caec3d8aee64536cda8aa952db07f6R46

> pythonではオーバーフローを考慮しなくて良いが、考慮する必要がある言語だと mid = left + (right - left) // 2のようにする。
>
- https://github.com/hayashi-ay/leetcode/pull/40/files
- これも前にやった気がする
- Pythonのint → Cのlong, Pythonのfloat → Cのdouble

```python
import sys

i = sys.maxsize
print(i)
# 9223372036854775807
print(i == i + 1)
# False
i += 1
print(i)
# 9223372036854775808

f = sys.float_info.max
print(f)
# 1.7976931348623157e+308
print(f == f + 1)
# True
f += 1
print(f)
# 1.7976931348623157e+308
```

- 今回は一つ一つの数字はユニークだが、連続する場合に前に入れようと思うとtarget == nums[middle]は要注意
- https://github.com/olsen-blue/Arai60/pull/41/files#diff-912c29ce0f491ca88b1474ced97db89f2d33a36a34cd0f516f5b523e400af176R67

## Step 3. Final Solution

- 閉区間

```python
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
middle = (left + right) // 2
if target <= nums[middle]:
right = middle - 1
Copy link

@potrue potrue Aug 13, 2025

Choose a reason for hiding this comment

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

right = middleのほうが個人的には良いと思いました。(target <= nums[middle]であってもmiddleが答えになりえるので)
これだと最後left == right + 1の状況になってleftが返されて終わりという感じになると思いますが、少しわかりにくいと感じました(捉え方によるのかもしれません)

else:
left = middle + 1
return left
```

- 開区間

Choose a reason for hiding this comment

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

個人的には、開区間か閉区間の選択ではなく、leftとrightがそれぞれ何を満たして欲しいものとして考えるかから始めることが重要な気がしています。

以下の方式であれば、
「rightは必ずtarget以上で、leftは必ずtarget未満である」というのが前提なので、停止するのはleftとrightが隣り合う時であり、targetが全要素より大きい/小さいを満たす初期値は〜、更新の仕方は〜と考えていくイメージです。


```python
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = -1
right = len(nums)
while left + 1 < right:
middle = (left + right) // 2
if target <= nums[middle]:
right = middle
else:
left = middle
return right
```

- 反対側の半開区間(-1, len(nums)-1]だけよく分からなかった