質問するログイン新規登録

回答編集履歴

4

めぐる式でコードを書き直す

2021/10/21 13:07

投稿

actorbug
actorbug

スコア2567

answer CHANGED
@@ -79,4 +79,25 @@
79
79
 
80
80
  left, right = binarySearchLeft(nums, target), binarySearchRight(nums, target)
81
81
  return [left, right] if left <= right else [-1, -1]
82
+ ```
83
+
84
+ ---
85
+ [二分探索アルゴリズムを一般化 〜 めぐる式二分探索法のススメ 〜](https://qiita.com/drken/items/97e37dd6143e33a64c8c)を読んで、やっとめぐる式の利点に気づいたので、今更ながら追記。
86
+
87
+ めぐる式で重要なのは、`solve`とそれが満たされる端`ok`、満たされない端`ng`さえ決まれば、あとは同じ書き方で済む点。半開区間か開区間かというのは重要ではなく、解きたい問題によって使い分ければいい。
88
+
89
+ 今回の問題では、開始位置は`nums[i] >= target`が満たされる最小の`i`により、終了位置は`nums[i] <= target`が満たされる最大の`i`により求まるので、このように書ける。
90
+
91
+ ```python
92
+ class Solution:
93
+ def searchRange(self, nums: List[int], target: int) -> List[int]:
94
+ def meguru(ok, ng, solve):
95
+ while abs(ok - ng) > 1:
96
+ mid = (ok + ng) // 2
97
+ if solve(mid): ok = mid
98
+ else: ng = mid
99
+ return ok
100
+ left = meguru(len(nums), -1, lambda i: nums[i] >= target)
101
+ right = meguru(-1, len(nums), lambda i: nums[i] <= target)
102
+ return [left, right] if left <= right else [-1, -1]
82
103
  ```

3

範囲の記述修正

2021/10/21 13:06

投稿

actorbug
actorbug

スコア2567

answer CHANGED
@@ -7,7 +7,7 @@
7
7
  ```
8
8
 
9
9
  `left`も`right`も`nums`の添え字として有効な値となっているので、これは「閉区間」です。
10
- 「閉区間」で`mid`を+1,-1しない実装だと、`left`と`right`の差1未満にならない場合があります。
10
+ 「閉区間」で`mid`を+1,-1しない実装だと、`left`と`right`に挟まれる範囲2未満にならない場合があります。
11
11
  そうなると、ループを抜けた後で`left`,`right`両方で条件判定して、正しい境界を見つけなければなりません。
12
12
  こちらの方針で質問者さんのコードを修正すると、以下のようになります。
13
13
  ```python

2

元のソースの修正例を追加

2021/10/17 08:14

投稿

actorbug
actorbug

スコア2567

answer CHANGED
@@ -1,4 +1,4 @@
1
- めぐる式「半開区間」を前提としていますが、質問者さんのコード「閉区間」で実装されているのがまずいのではないでしょうか。
1
+ めぐる式「半開区間」を前提としているのに、質問者さんのコード「閉区間」で実装されているのがまずいのではないでしょうか。
2
2
 
3
3
  質問者さんのコードだと、以下のように範囲をとっています。
4
4
  ```python
@@ -9,9 +9,55 @@
9
9
  `left`も`right`も`nums`の添え字として有効な値となっているので、これは「閉区間」です。
10
10
  「閉区間」で`mid`を+1,-1しない実装だと、`left`と`right`の差が1未満にならない場合があります。
11
11
  そうなると、ループを抜けた後で`left`,`right`両方で条件判定して、正しい境界を見つけなければなりません。
12
+ こちらの方針で質問者さんのコードを修正すると、以下のようになります。
13
+ ```python
14
+ class Solution:
15
+ def searchRange(self, nums: List[int], target: int) -> List[int]:
16
+ if not nums:
17
+ return [-1, -1]
18
+ # find end
19
+ left = 0
20
+ right = len(nums) - 1
21
+ while right - left > 1:
22
+ mid = left + ((right - left) // 2)
23
+ if self.isin_left_area(nums[mid], target):
24
+ right = mid
25
+ else:
26
+ left = mid
27
+ if self.isin_left_area(nums[left], target):
28
+ end = left - 1
29
+ elif self.isin_left_area(nums[right], target):
30
+ end = left
31
+ else:
32
+ end = right
12
33
 
34
+ # find start
35
+ left = 0
36
+ right = len(nums) - 1
37
+ while right - left > 1:
38
+ mid = left + ((right - left) // 2)
39
+ if self.isin_right_area(nums[mid], target):
40
+ left = mid
41
+ else:
42
+ right = mid
43
+ if self.isin_right_area(nums[right], target):
44
+ start = right + 1
45
+ elif self.isin_right_area(nums[left], target):
46
+ start = right
47
+ else:
48
+ start = left
49
+
50
+ return [start, end] if start <= end else [-1, -1]
51
+
52
+ def isin_left_area(self, mid_val:int, target:int):
53
+ return mid_val > target
54
+
55
+ def isin_right_area(self, mid_val:int, target:int):
56
+ return mid_val < target
57
+ ```
58
+
13
59
  めぐる式の「半開区間」でも、ループを抜けた後の条件判定が一度は必要になるので、めぐる式のベースである蟻本で採用している「開区間」を採用する方が、正直なところ実装は楽だと思います。
14
- `left`、`right`とも`nums`の添え字の範囲外であることに注意してください。
60
+ 以下のソースで、`left`、`right`とも`nums`の添え字の範囲外であることに注意してください。
15
61
  ```python
16
62
  class Solution:
17
63
  def searchRange(self, nums, target):

1

区間の説明

2021/10/17 08:08

投稿

actorbug
actorbug

スコア2567

answer CHANGED
@@ -1,5 +1,18 @@
1
- めぐる式についてよくわかりせんが、「ここまでだと蟻本と同じと言及されている蟻本実装を使えば、以下に書けます
1
+ めぐる式は「半開区間」を前提としていが、質問者さんのコードは閉区間で実装されているのがまずいではないでしょ
2
+
3
+ 質問者さんのコードだと、以下のように範囲をとっています。
2
4
  ```python
5
+ left = 0
6
+ right = len(nums) - 1
7
+ ```
8
+
9
+ `left`も`right`も`nums`の添え字として有効な値となっているので、これは「閉区間」です。
10
+ 「閉区間」で`mid`を+1,-1しない実装だと、`left`と`right`の差が1未満にならない場合があります。
11
+ そうなると、ループを抜けた後で`left`,`right`両方で条件判定して、正しい境界を見つけなければなりません。
12
+
13
+ めぐる式の「半開区間」でも、ループを抜けた後の条件判定が一度は必要になるので、めぐる式のベースである蟻本で採用している「開区間」を採用する方が、正直なところ実装は楽だと思います。
14
+ `left`、`right`とも`nums`の添え字の範囲外であることに注意してください。
15
+ ```python
3
16
  class Solution:
4
17
  def searchRange(self, nums, target):
5
18
  def binarySearchLeft(A, x):