Skip to content

33. Search in Rotated Sorted Array #44

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

fhiyo
Copy link
Owner

@fhiyo fhiyo commented Jul 17, 2024

- https://discord.com/channels/1084280443945353267/1233295449985650688/1239440783824781362
- https://github.com/Exzrgs/LeetCode/pull/8

一気に二分探索をするでも解けるようだ...一応書きはしたが難しい。 `nums[mid] == target` の条件がないと書けなかった。
Copy link

Choose a reason for hiding this comment

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

Copy link
Owner Author

Choose a reason for hiding this comment

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

見ましたが、よく分かってないままスルーしちゃってました。見返したら分かった気がします。keyを上手いこと設計するとキレイな二分探索に落とし込めるんですね...

リンク先のおしりと比べると一つ比較が減らせるというやつは、頭と比較する場合はtargetが nums[-1] < target < nums[0] の範囲にあるときにはtargetが+2, リスト内がすべて+1以下になるからはみ出すが、おしりと比べたらそのケースが無くなるから、という理解ですが合ってますかね...?

Copy link

Choose a reason for hiding this comment

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

あ、そうです。


offset = get_offset(nums)
low = 0
high = len(nums)
Copy link

Choose a reason for hiding this comment

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

二度目の二分探索も get_offset() と同様に [low, high] で区間を取ると、思考を使い回せるので読むのが楽になると思いました。

if target_index1 != -1:
return target_index1
target_index2 = search_helper(partition, len(nums))
if target_index2 != -1:
Copy link

Choose a reason for hiding this comment

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

target_index1 の場合と形をそろえてあるのは読みやすく感じました。(自分だと94~96行をまとめて return target_index2 としてしまいそうです。)

あとあとの変更のしやすさを考えても return target_index2 とせずに、冗長にしておくのはよいですね。(今回のように、呼び出し元と呼び出す関数の返り値が同じとは限らないので。)

class Solution:
def search(self, nums: List[int], target: int) -> int:
def get_offset(nums: list[int]) -> int:
assert len(nums) > 0
Copy link

Choose a reason for hiding this comment

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

ここの assert 文は get_offset() の中ではなく、search() の直下にある方が自然に感じます。
自然言語で書くなら、
「get_offset() を呼び出して、実引数が無効で停止するか、有効なら値を得る」と、
「実引数が無効でないことを確認して、get_offset() を呼び出して有効な値を得る」
の違いといったらよいでしょうか。

(step3では search の冒頭で nums == [] の場合をケアされているので、余計なコメントでしたらすみません。)

Copy link
Owner Author

Choose a reason for hiding this comment

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

うーむ、呼び出す側と呼ばれる側どちらが不正な状態をチェックするかという話ですよね...たしかにget_offset()を呼び出すことを選択してる時点で引数が空でないことはチェックしていてもいいかもしれないです。

ただ「引数のリストが空でないこと」はget_offset()の契約内容な気がしており、search()全体ではnumsは空でも構わないように見えたんですよね (なのでstep3では別途処理をした)。
呼び出す側でチェックするならassertじゃなく例外処理にする選択肢はあるかもなと思いました。空リストは入力として来うるがそれは正常系以外で処理をするよという宣言をして、get_offset()内では変わらず空リストでないことを事前条件として強制する。

Copy link

@hayashi-ay hayashi-ay left a comment

Choose a reason for hiding this comment

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

良いと思います。個人的には offsetを見つける -> targetが含まれる可能性がある範囲について2分探索をすると素直に2つのStepに分けるのが分かりやすいかなと思います。

return (index + offset) % len(nums)

offset = get_offset(nums)
low = 0

Choose a reason for hiding this comment

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

ここの2分探索のlow, high、rotateしていない昇順に並んだ配列を想定してその配列に対してのindexという感じですかね?なんか結構処理がトリッキーな感じがします。Step2のbisect_leftの解法の方が理解しやすい気がします。

high = len(nums)
while low < high:
mid = (low + high) // 2
if nums[get_original_index(mid, offset)] >= target:

Choose a reason for hiding this comment

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

get_original_indexという命名は少し混乱しました。この関数だと1のインデックスから2のインデックスを求めていると思うのですが、1と2がどちらがOriginalかは捉え方で変わりそうな気がしてます。自分は1がOriginalだと思ってコードを読んでしまっていたのでかなり理解が難しかったです。

  1. Rotate前の昇順に並んだ配列
  2. Rotateした後の配列 <- search関数の引数numsで渡されるもの

Copy link
Owner Author

Choose a reason for hiding this comment

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

1と2がどちらがOriginalかは捉え方で変わりそうな気がしてます

ちょっとその危険はあるな...と思いつつより良い名前が思いつかなかったのでこの名前にしてました...
get_original_index内にrotate前のindexを求める関数だよという説明コメントがあれば良かったかもですね

```py
class Solution:
def search(self, nums: List[int], target: int) -> int:
def get_offset(nums: list[int]) -> int:

Choose a reason for hiding this comment

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

細かいんですが, typing libraryのListで型アノテーションをしているので, そちらに合わせた方がいいかもです。

Copy link
Owner Author

Choose a reason for hiding this comment

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

https://docs.python.org/3.11/library/typing.html#typing.List

Deprecated alias to list.

deprecatedなのであまり使いたくないんですよね。leetcodeに書いてあるやつはいちいち直してはいませんが。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants