-
Notifications
You must be signed in to change notification settings - Fork 0
Solved Arai60/35. Search Insert Position #41
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,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: | ||
| 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 | ||
|
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. right = middleのほうが個人的には良いと思いました。(target <= nums[middle]であってもmiddleが答えになりえるので) |
||
| else: | ||
| left = middle + 1 | ||
| return left | ||
| ``` | ||
|
|
||
| - 開区間 | ||
|
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. 個人的には、開区間か閉区間の選択ではなく、leftとrightがそれぞれ何を満たして欲しいものとして考えるかから始めることが重要な気がしています。 以下の方式であれば、
Owner
Author
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. その前提を決めることが開区間か閉区間かを決めることに相当するような気がしているのですが違うのでしょうか? ちなみにこのやり方は、以前SWE協会の勉強会における模擬面接で「この二分探索を開区間で書けますか?」というような質疑をしていたのを思い出してのことです。 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. 背景として、「開区間」「閉区間」という単語の意味を分からないまま、二分探索のアルゴリズムの理解に使用しようとしている方がいらっしゃいました。単語の意味を理解しないまま理解を進めようとしたため、二分探索自体も理解できていませんでした。そのため、 SWE 協会の過去のレビューに置いては、開区間・閉区間という単語を使わず、以下を明示的に理解することから始めるという流れができたように思います。
ご自身が書かれたコードについて、上記を一通り答えられるか確認することをお勧めいたします。また、他の参加者の過去の回答を読み、上記について一通り答えられるかどうか確認すると、理解が深まると思います。 二分探索は、自分なりの書き方で書くことができ、かつ、他の方が書かれた書き方を読んで理解できることが望ましいと思います。 |
||
|
|
||
| ```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]だけよく分からなかった | ||
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.
関数名には、どのような値が返ってくるかを表す名前を付けたほうが、ソースコードが理解しやすくなると思います。