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
79 changes: 79 additions & 0 deletions 35. Search Insert Position/35. Search Insert Position.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 35. Search Insert Position
## STEP1
- 何も見ずに解いてみる
- ライブラリを使う(これは求められていないはず)
```python
import bisect


class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
return bisect.bisect_left(nums, target)
```

- 二分探索を書く
- left 以下のインデックスは target 未満、right 以上のインデックスは target 以上を満たすように書く。left と right が隣り合ったら right を返して終了。
```python
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = -1
right = len(nums)
while left < right - 1:
middle = (left + right) // 2
if nums[middle] < target:
left = middle
else:
right = middle
return right
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

いいと思います。
leftは必ずtarget未満で、rightがtarget以上という不変条件を保ちながら更新し、最終的にleftとrightが隣り合って終了するのですね。

```

## STEP2
### プルリクやドキュメントを参照
- https://github.com/olsen-blue/Arai60/pull/41/files
- 再帰でも書ける。while 文の方がシンプルな気がした。
- https://discord.com/channels/1084280443945353267/1196498607977799853/1269532028819476562
に従いSTEP1のコードを説明してみる。
1. 二分探索を、 [false, false, false, ..., false, true, true, ture, ..., true] と並んだ配列があったとき、 一番左の true の位置を求める問題と捉える。true がない場合は len(nums) を返す。
2. 位置を求めるにあたり、答えが含まれる範囲を狭めていく問題と捉える。
3. 範囲は半開区間で考える。(left, right] に求める index が入っているように設定する。
4. 初期値について:left と right が隣り合った時 (left + 1 = right) に終了し right を返すので left の初期値は -1, right の初期値は len(nums) とする。
5. ループ不変条件について:4より left < right - 1。left 以下の index については false が確定、right 以上の index については true が確定している。
6. 範囲を狭めるためのロジックについて:ループ中は left < right - 1 のため、middle = (left + right) // 2 は、left < middle < right である。nums[middle] < target の場合、5より left を middle とする。nums[middle] >= target の場合は right をmiddle とする。left < middle < right なので必ず範囲は狭まる。

- STEP1 では left = -1 である点と return right である点、が微妙に感じる引っかかるポイント。

- 以下は違うパターン。求める index が [left, right] に存在するように設定。初期値は自然に left = 0, right = len(nums) になる。left = right となったら終了。left 未満の index は target 未満、right 以上の index は target 以上となるようにする。nums[middle] < target の時は、middle で target 未満なので left はその右(+1)にする。right の位置では target 以上を保証しないといけないのでright = middleでなければいけない。右に狭める時は少なくとも +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.

自分もこちらのパターンの方が直観的で好みですね。

- 初期値がわかりやすい。left 更新時に +1 が必要。ループで保証すべきことを理解していれば +1 を忘れないのでこちらの方が良いと思った。
```python
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)
while left < right:
middle = (left + right) // 2
if nums[middle] < target:
left = middle + 1
else:
right = middle
return left
```

- false と true の境界の位置を求めるというのはあまりイメージが湧かなかった。すぐ上のコードを半開区間らしいがあまりそうは感じない。区間の取り方をどのようにしても、ループ終了時に一つの要素(区間の区切り)を指していて欲しいという気持ちがあります。
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

すぐ上のコードを半開区間らしいがあまりそうは感じない。

right = len(nums) と範囲外に定義しているので、right が含まれない (半開区間) と想像されますね。

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.

コメントありがとうございます。
挿入する位置を探しているので right = len(nums) も解の候補としては範囲内と私は捉えている(以下A)のですがあまり主流ではなさそうです。Bはあまりしっくりきていないのですが、より適切な表現があれば教えていただきたいです。
A
[left, right] に挿入位置が含まれる。初期値は left = 0, right = len(nums)。left 未満、right より大きい index は解にならない(left 未満 の index では target より小さい、right 以上の index では target 以上か right = len(nums) である)。left = right になれば終了([left, right] には挿入位置が含まれ、候補が一つになったため)。
B
[left, right) について target との大小関係を調べる。初期値は left = 0, right = len(nums)。left 未満の index では target より小さい、right 以上の index では target 以上か right = len(nums) 。left = right になれば終了(探索すべき範囲が無くなったので left が求める解、left = len(nums) になりうる)。


## STEP3
### 3回ミスなく書く
```python
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)
while left < right:
middle = (left + right) // 2
if nums[middle] < target:
left = middle + 1
else:
right = middle
return left
```

1分,1分,1分で3回Accept