-
Notifications
You must be signed in to change notification settings - Fork 0
35. Search Insert Position #43
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,159 @@ | ||
| # Step1 | ||
|
|
||
| かかった時間:2min | ||
|
|
||
| 計算量:nums.length=Nとして | ||
|
|
||
| 時間計算量:O(logN) | ||
|
|
||
| 空間計算量:O(1) | ||
|
|
||
|
|
||
| ```python | ||
| import bisect | ||
|
|
||
|
|
||
| class Solution: | ||
| def searchInsert(self, nums: List[int], target: int) -> int: | ||
| return bisect.bisect_left(nums, target) | ||
| ``` | ||
| 思考ログ: | ||
| - bisect周りの仕様を復習しよう | ||
|
|
||
| シンプルに探索 | ||
| ```python | ||
| class Solution: | ||
| def searchInsert(self, nums: List[int], target: int) -> int: | ||
| for i, num in enumerate(nums): | ||
| if target <= num: | ||
| return i | ||
|
|
||
| return len(nums) | ||
| ``` | ||
| 思考ログ: | ||
|
|
||
| 再帰 | ||
| ```python | ||
| class Solution: | ||
| def searchInsert(self, nums: List[int], target: int) -> int: | ||
| if len(nums) == 1: | ||
| if nums[0] < target: | ||
| return 1 | ||
| else: | ||
| return 0 | ||
|
|
||
| split_index = len(nums) // 2 | ||
| if target >= nums[split_index]: | ||
| return split_index + self.searchInsert(nums[split_index:], target) | ||
| else: | ||
| return self.searchInsert(nums[:split_index], target) | ||
| ``` | ||
| - クイックセレクトを思い出した | ||
|
|
||
| # Step2 | ||
|
|
||
| 講師役目線でのセルフツッコミポイント: | ||
|
|
||
| 参考にした過去ログなど: | ||
| - https://github.com/Ryotaro25/leetcode_first60/pull/45 | ||
| - https://github.com/seal-azarashi/leetcode/pull/38 | ||
| - left/rightの位置について、何が言えるか | ||
| - 停止性問題 | ||
| - https://github.com/Mike0121/LeetCode/pull/43 | ||
| - https://github.com/Yoshiki-Iwasa/Arai60/pull/34 | ||
| - https://github.com/nittoco/leetcode/pull/28 | ||
| - bisectのコード | ||
| - https://github.com/python/cpython/blob/3.12/Lib/bisect.py | ||
| - https://github.com/fhiyo/leetcode/pull/42 | ||
| - https://github.com/sakupan102/arai60-practice/pull/42 | ||
| - https://github.com/rm3as/code_interview/pull/5 | ||
| - https://github.com/shining-ai/leetcode/pull/41 | ||
| - https://github.com/hayashi-ay/leetcode/pull/40 | ||
| - Arrays.binarySearch(java)を使った解法 | ||
| - bit反転と2の補数表現 | ||
|
|
||
| bisect使わず2分探索 | ||
| ```python | ||
| class Solution: | ||
| def searchInsert(self, nums: List[int], target: int) -> int: | ||
| def is_larger_than_target(i: int) -> bool: | ||
|
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. (間が空いてしまいすみません) |
||
| return target <= nums[i] | ||
|
|
||
| left = 0 | ||
| right = len(nums) | ||
| while left < right: | ||
| middle = (left + right) // 2 | ||
| if is_larger_than_target(middle): | ||
| right = middle | ||
| else: | ||
| left = middle + 1 | ||
|
|
||
| return left | ||
| ``` | ||
| 思考ログ: | ||
| - 二分探索についてはdiscord上でかなり議論されているので、別途検索をかけてみる | ||
|
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. 二分探索は何をループの不変条件とするかによっていろいろな書き方があるんですが、不変条件を理解せずに書いている人が多いということです。 理解せずに書くためには書き方を固定すれば、ある程度は書けるようになります。 書く時は自分の書き方を固定してもいいんですが、読むときには、不変条件をどのような形で書いてくるかを指定できないので、どんな書き方がされていても動くのか動かないのかも含めて読めないといけない、というように読むほうがだいぶ難しいという話かと思います。
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. 今回、色々見て回ってとても勉強になりました。 白状すると、二分探索は自分も書き方を固定して使っていました。 そのせいで、型から外れたコードを読むのには、少なからず抵抗感があったと自覚してます。 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. まずは「何を探しているのか」「ループごとに今どこまで分かっているのか」「それをどう変数に表現しているのか」というのが意味の部分の話で、あとはループの中で「終了条件」「更新」「必ず終了すること」という形式操作の話くらいです。 だいたい、意味をすっ飛ばして形式だけでやろうとするので、「左」「右」「閉区間」などと唱えたら倒せると思っているんですが、もっと単純に、二分探索の仕事を途中で引き継いだら知りたいことは何なのか「昇順で並んでいる数字の中で一番左の400以上の数を探していて、いくつか確認した。重要な知見として、25番目が288で、51番目が789、その間は開いていない。」くらいですよね。で、この情報を圧縮して変数とコードにするだけです。
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. ACすることが目的になれば、等号不等号の組み合わせを全探索したり、特定の型を暗記した方がコスパが良い、ということですよね。 |
||
| - 二分探索について、もう少し一般化して思考の痕を残しておく | ||
| - とある条件を満たす集合Xと満たさない集合X^cを考える | ||
| - 今回のお題ではtarget以上の数が入っている配列のインデックス集合をXとおく | ||
| - 探索する配列(arrとする)の要素について、ある整数kが存在して、arr[i]∈X^c (i<=k)、arr[i]∈X (i>k)、とできるとする(前提条件) | ||
| - 今回のお題では配列がソートされているのでこの前提条件を満たす | ||
| - 探索インデックスの範囲を[left, right)と設定する | ||
| - mid = (left + right) // 2を計算し、midが条件を満たすかどうか確認して、探索範囲を更新する | ||
| - left未満のインデックスはX^cの要素、right以上のインデックスはXの要素となるように更新する | ||
| - mid∈X^cの時、left = mid + 1 | ||
| - mid∈Xの時、right = mid | ||
| - 探索範囲は単調に縮まり最終的にleft==rightとなりループが停止する | ||
| - この時left==rightのインデックスは、Xの下限となっている(X^cの上限でもある) | ||
| - パラパラ漫画も作ってみた | ||
| - https://docs.google.com/presentation/d/1QABhB8fFeR5nt8x58dpSa3R3cNGzZ4Rcnmj-1cX74qA/edit#slide=id.p | ||
|
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. 自分ごとですが、二分探索はとても苦手(現在進行形で苦戦してます)なのでビジュアルの説明と思考の文書化はとても勉強になります🙇
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. ありがとうございます。 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. パラパラ漫画わかりやすかったです。 |
||
|
|
||
| pivotをランダムにしてみる | ||
| ```python | ||
| class Solution: | ||
| def searchInsert(self, nums: List[int], target: int) -> int: | ||
| def is_larger_than_target(i: int) -> bool: | ||
| return target <= nums[i] | ||
|
|
||
| left = 0 | ||
| right = len(nums) | ||
| while left < right: | ||
| pivot = random.randint(left, right - 1) | ||
| if is_larger_than_target(pivot): | ||
| right = pivot | ||
| else: | ||
| left = pivot + 1 | ||
|
|
||
| return left | ||
| ``` | ||
| 思考ログ: | ||
|
|
||
| # Step3 | ||
|
|
||
| かかった時間:2min | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def searchInsert(self, nums: List[int], target: int) -> int: | ||
| def is_larger_than_target(i: int) -> bool: | ||
| return target <= nums[i] | ||
|
|
||
| left = 0 | ||
| right = len(nums) | ||
| while left < right: | ||
| middle = (left + right) // 2 | ||
| if is_larger_than_target(middle): | ||
| right = middle | ||
| else: | ||
| left = middle + 1 | ||
|
|
||
| return left | ||
| ``` | ||
| 思考ログ: | ||
| - pythonでは心配はないが、オーバーフローを気にする場合はleft + (right - left) // 2の選択肢も頭においておく | ||
| - left <= middle < rightになっているのも大事なポイント | ||
|
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. このポイントですが常にこうなっているということでしょうか? 自分は現在ここに苦戦していますが。。。🙇
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. コメントありがとうございます。 この時、left = ⌊(left + left + 1) / 2⌋ <= ⌊(left + right) / 2⌋ <= (left + right) / 2 < right |
||
|
|
||
| # Step4 | ||
|
|
||
| ```python | ||
| ``` | ||
| 思考ログ: | ||
Uh oh!
There was an error while loading. Please reload this page.
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.
今まで気づかなかったのですがPythonにはInner Functionがあるのですね。
https://google.github.io/styleguide/pyguide.html#26-nestedlocalinner-classes-and-functions
Google Guideにも記載ございましたが今回のように使うのですね!
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.
これは、正直に言うと実は私の意識が足りていないです()
もう少し気をつけて使います。