diff --git a/153. Find Minimum in Rotated Sorted Array/153. Find Minimum in Rotated Sorted Array.md b/153. Find Minimum in Rotated Sorted Array/153. Find Minimum in Rotated Sorted Array.md new file mode 100644 index 0000000..114c7fd --- /dev/null +++ b/153. Find Minimum in Rotated Sorted Array/153. Find Minimum in Rotated Sorted Array.md @@ -0,0 +1,74 @@ +# 153. Find Minimum in Rotated Sorted Array +## STEP1 +- 何も見ずに解いてみる +- [left, right] に最小値となる index が含まれていて、それ以外には含まれていないことを保証して範囲を狭める。初期値は left = 0, right = len(nums) - 1 とする。 nums[left] より nums[middle] の方が小さい場合、[left, middle] で ..., nums[n - 1], nums[0], ... となっている箇所があるため、この中に最小値が含まれる。そうでない場合、区間全体で単調増加 (nums[left] < nums[right]) していれば left が求める index であり、そうでない場合は middle より右側に求める index がある。nums[middle] は nums[left] より大きいので +1 してよい。 +```python +class Solution: + def findMin(self, nums: List[int]) -> int: + left = 0 + right = len(nums) - 1 + while left < right: + middle = (left + right) // 2 + if nums[middle] < nums[left]: + right = middle + continue + if nums[left] < nums[right]: + break + left = middle + 1 + return nums[left] +``` +## STEP2 +### プルリクやドキュメントを参照 +- https://github.com/olsen-blue/Arai60/pull/42/files + - nums[right] もしくは nums[-1] と比較した方が直接的。nums[middle] < nums[right] の場合、[middle, right] に切れ目があろうがなかろうが middle 以下の範囲に最小値がある。left と比較するとそうはいかない。 +- https://discord.com/channels/1084280443945353267/1230079550923341835/1233971372946882600 + > nums[0] <= nums[i] な領域と nums[0] > nums[i] な領域の境界を探せ + nums[-1] < nums[i] な領域と nums[-1] >= nums[i] な領域の境界を探せ + - このように捉えると普通の二分探索。 + - STEP1では無理やり探索している感がある。これは[false, false, false, ..., false, true, true, ture, ..., true]から左の true を探す問題という設定をしていなかったためですね。。。 +- 考え直して解く。 + - nums[-1] 以下である一番左の index を求める。[left, right] に求める index が含まれるようにする。left より小さい index では nums[-1] 以上が確定、right 以上の index では nums[-1] 以下が確定している。 +```python +class Solution: + def findMin(self, nums: List[int]) -> int: + left = 0 + right = len(nums) - 1 + while left < right: + middle = (left + right) // 2 + if nums[middle] <= nums[-1]: + right = middle + else: + left = middle + 1 + return nums[left] +``` +- https://discord.com/channels/1084280443945353267/1230079550923341835/1235694567085576275 + - bisect を使うパターン。 + - 2番目は、オペレータ ge に partial を使って部分的に引数を代入して比較する関数を作り出している。 + - https://docs.python.org/3/library/operator.html + - https://docs.python.org/3/library/functools.html#functools.partial + - 3番目は、先頭の要素と比較している。単調増加の場合は全て False になり bisect_right の結果は len(nums) となり、返り値は nums[0] で先頭を返す。rotate している場合はその箇所で false, true となる。bisect_right の結果はそのままで良いが、単調増加の場合との場合分けを減らすために - len(nums) としている。 + - なるほど、先頭と比較しても場合分け増えずに書けるが、index の取り扱いがややトリッキーな気がした。 +```python + def findMin(self, nums: List[int]) -> int: + return nums[bisect_left(nums, True, key=lambda x: x <= nums[-1])] + return nums[bisect_left(nums, True, key=partial(ge, nums[-1]))] + return nums[bisect_right(nums, False, key=lambda x: x < nums[0]) - len(nums)] +``` + +## STEP3 +### 3回ミスなく書く +```python +class Solution: + def findMin(self, nums: List[int]) -> int: + left = 0 + right = len(nums) - 1 + while left < right: + middle = (left + right) // 2 + if nums[middle] <= nums[-1]: + right = middle + else: + left = middle + 1 + return nums[left] +``` + +3分,1分,1分で3回Accept