Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a41eeda

Browse files
committed
(l + r) // 2 to l + (r - l) // 2; improved doc; added hidden binary search problems
1 parent b77a5b7 commit a41eeda

File tree

1 file changed

+56
-87
lines changed

1 file changed

+56
-87
lines changed

‎basic_algorithm/binary_search.md

Lines changed: 56 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,29 @@
11
# 二分搜索
22

3-
## 二分搜索模板
3+
给一个**有序数组**和目标值,找第一次/最后一次/任何一次出现的索引,时间复杂度 O(logN)。
44

5-
给一个**有序数组**和目标值,找第一次/最后一次/任何一次出现的索引,如果没有出现返回-1
5+
## 模板
66

7-
模板四点要素
7+
常用的二分搜索模板有如下三种形式:
88

9-
- 1、初始化:start=0、end=len-1
10-
- 2、循环退出条件:start + 1 < end
11-
- 3、比较中点和目标值:A[mid] ==、 <、> target
12-
- 4、判断最后两个元素是否符合:A[start]、A[end] ? target
13-
14-
时间复杂度 O(logn),使用场景一般是有序数组的查找
9+
![binary_search_template](https://img.fuiboom.com/img/binary_search_template.png)
1510

16-
典型示例
11+
其中,模板 1 和 3 是最常用的,几乎所有二分查找问题都可以用其中之一轻松实现。模板 2 更高级一些,用于解决某些类型的问题。详细的对比可以参考 Leetcode 上的文章:[二分搜索模板](https://leetcode-cn.com/explore/learn/card/binary-search/212/template-analysis/847/)
1712

1813
### [binary-search](https://leetcode-cn.com/problems/binary-search/)
1914

2015
> 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
2116
17+
- 模板 3 的实现
18+
2219
```Python
2320
class Solution:
2421
def search(self, nums: List[int], target: int) -> int:
2522

2623
l, r = 0, len(nums) - 1
2724

2825
while l + 1 < r:
29-
mid = (l + r) // 2
26+
mid = l + (r - l) // 2
3027
if nums[mid] < target:
3128
l = mid
3229
else:
@@ -40,25 +37,16 @@ class Solution:
4037
return -1
4138
```
4239

43-
大部分二分查找类的题目都可以用这个模板,然后做一点特殊逻辑即可
44-
45-
另外二分查找还有一些其他模板如下图,大部分场景模板 3 都能解决问题,而且还能找第一次/最后一次出现的位置,应用更加广泛
46-
47-
![binary_search_template](https://img.fuiboom.com/img/binary_search_template.png)
48-
49-
所以用模板#3 就对了,详细的对比可以这边文章介绍:[二分搜索模板](https://leetcode-cn.com/explore/learn/card/binary-search/212/template-analysis/847/)
50-
51-
- 如果是最简单的二分搜索,不需要找第一个、最后一个位置、或者是没有重复元素,可以使用模板 1,代码更简洁
40+
- 如果是最简单的二分搜索,不需要找第一个、最后一个位置,或者是没有重复元素,可以使用模板 1,代码更简洁。同时,如果搜索失败,left 是第一个大于 target 的索引,right 是最后一个小于 target 的索引。
5241

5342
```Python
54-
# 无重复元素搜索时,更方便
5543
class Solution:
5644
def search(self, nums: List[int], target: int) -> int:
5745

5846
l, r = 0, len(nums) - 1
5947

6048
while l <= r:
61-
mid = (l + r) // 2
49+
mid = l + (r - l) // 2
6250
if nums[mid] == target:
6351
return mid
6452
elif nums[mid] > target:
@@ -67,12 +55,9 @@ class Solution:
6755
l = mid + 1
6856

6957
return -1
70-
# 如果找不到,start 是第一个大于target的索引
71-
# 如果在B+树结构里面二分搜索,可以return start
72-
# 这样可以继续向子节点搜索,如:node:=node.Children[start]
7358
```
7459

75-
- 模板 2:
60+
- 模板 2 的实现
7661

7762
```Python
7863
class Solution:
@@ -81,7 +66,7 @@ class Solution:
8166
l, r = 0, len(nums) - 1
8267

8368
while l < r:
84-
mid = (l + r) // 2
69+
mid = l + (r - l) // 2
8570
if nums[mid] < target:
8671
l = mid + 1
8772
else:
@@ -93,18 +78,15 @@ class Solution:
9378
return -1
9479
```
9580

96-
97-
9881
## 常见题目
9982

10083
### [find-first-and-last-position-of-element-in-sorted-array](https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)
10184

10285
> 给定一个包含 n 个整数的排序数组,找出给定目标值 target 的起始和结束位置。如果目标值不在数组中,则返回`[-1, -1]`
10386
104-
- 思路:核心点就是找第一个 target 的索引,和最后一个 target 的索引,所以用两次二分搜索分别找第一次和最后一次的位置
87+
- 思路:核心点就是找第一个 target 的索引,和最后一个 target 的索引,所以用两次二分搜索分别找第一次和最后一次的位置,下面是使用模板 3 的解法
10588

10689
```Python
107-
# 使用模板3的解法
10890
class Solution:
10991
def searchRange(self, nums, target):
11092
Range = [-1, -1]
@@ -113,7 +95,7 @@ class Solution:
11395

11496
l, r = 0, len(nums) - 1
11597
while l + 1 < r:
116-
mid = (l + r) // 2
98+
mid = l + (r - l) // 2
11799
if nums[mid] < target:
118100
l = mid
119101
else:
@@ -128,7 +110,7 @@ class Solution:
128110

129111
l, r = 0, len(nums) - 1
130112
while l + 1 < r:
131-
mid = (l + r) // 2
113+
mid = l + (r - l) // 2
132114
if nums[mid] <= target:
133115
l = mid
134116
else:
@@ -153,7 +135,7 @@ class Solution:
153135

154136
l, r = 0, len(nums) - 1
155137
while l < r:
156-
mid = (l + r) // 2
138+
mid = l + (r - l) // 2
157139
if nums[mid] < target:
158140
l = mid + 1
159141
else:
@@ -166,7 +148,7 @@ class Solution:
166148

167149
l, r = 0, len(nums) - 1
168150
while l < r:
169-
mid = (l + r + 1) // 2
151+
mid = l + (r - l + 1) // 2
170152
if nums[mid] > target:
171153
r = mid - 1
172154
else:
@@ -180,7 +162,7 @@ class Solution:
180162

181163
> 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
182164
183-
思路:使用模板 1,若不存在,随后的左边界为第一个大于目标值的索引(插入位置),右边界为最后一个小于目标值的索引
165+
-使用模板 1,若不存在,左边界为第一个大于目标值的索引(插入位置),右边界为最后一个小于目标值的索引
184166

185167
```Python
186168
class Solution:
@@ -189,7 +171,7 @@ class Solution:
189171
l, r = 0, len(nums) - 1
190172

191173
while l <= r:
192-
mid = (l + r) // 2
174+
mid = l + (r - l) // 2
193175
if nums[mid] == target:
194176
return mid
195177
elif nums[mid] > target:
@@ -204,10 +186,11 @@ class Solution:
204186

205187
> 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
206188
>
207-
> - 每行中的整数从左到右按升序排列。
208-
> - 每行的第一个整数大于前一行的最后一个整数。
189+
> 1. 每行中的整数从左到右按升序排列。
190+
>
191+
> 2. 每行的第一个整数大于前一行的最后一个整数。
209192
210-
思路:两次二分,首先定位行数,接着定位列数
193+
-两次二分,首先定位行数,接着定位列数
211194

212195
```Python
213196
class Solution:
@@ -219,7 +202,7 @@ class Solution:
219202
l, r = 0, len(matrix) - 1
220203

221204
while l <= r:
222-
mid = (l + r) // 2
205+
mid = l + (r - l) // 2
223206
if matrix[mid][0] == target:
224207
return True
225208
elif matrix[mid][0] < target:
@@ -230,7 +213,7 @@ class Solution:
230213
row = r
231214
l, r = 0, len(matrix[0]) - 1
232215
while l <= r:
233-
mid = (l + r) // 2
216+
mid = l + (r - l) // 2
234217
if matrix[row][mid] == target:
235218
return True
236219
elif matrix[row][mid] < target:
@@ -241,36 +224,11 @@ class Solution:
241224
return False
242225
```
243226

244-
### [first-bad-version](https://leetcode-cn.com/problems/first-bad-version/)
245-
246-
> 假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
247-
> 你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
248-
249-
```Python
250-
class Solution:
251-
def firstBadVersion(self, n):
252-
253-
l, r = 1, n
254-
255-
while l + 1 < r:
256-
mid = (l + r) // 2
257-
if isBadVersion(mid):
258-
r = mid
259-
else:
260-
l = mid
261-
262-
if isBadVersion(l):
263-
return l
264-
else:
265-
return r
266-
```
267-
268227
### [find-minimum-in-rotated-sorted-array](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/)
269228

270-
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
271-
> 请找出其中最小的元素。假设数组中无重复元素。
229+
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转,例如,数组 [0, 1, 2, 4, 5, 6, 7] 可能变为 [4, 5, 6, 7, 0, 1, 2]。请找出其中最小的元素。假设数组中无重复元素。
272230
273-
思路:使用二分搜索,当中间元素大于右侧元素时意味着拐点即最小元素在右侧,否则在左侧
231+
-使用二分搜索,当中间元素大于右侧元素时意味着拐点即最小元素在右侧,否则在左侧
274232

275233
```Python
276234
class Solution:
@@ -279,7 +237,7 @@ class Solution:
279237
l , r = 0, len(nums) - 1
280238

281239
while l < r:
282-
mid = (l + r) // 2
240+
mid = l + (r - l) // 2
283241
if nums[mid] > nums[r]: # 数组有重复时,若 nums[l] == nums[mid] == nums[r],无法判断移动方向
284242
l = mid + 1
285243
else:
@@ -290,9 +248,7 @@ class Solution:
290248

291249
### [find-minimum-in-rotated-sorted-array-ii](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/)
292250

293-
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转
294-
> ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
295-
> 请找出其中最小的元素。(包含重复元素)
251+
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转,例如,数组 [0, 1, 2, 4, 5, 6, 7] 可能变为 [4, 5, 6, 7, 0, 1, 2]。请找出其中最小的元素。数组中可能包含重复元素。
296252
297253
```Python
298254
class Solution:
@@ -301,7 +257,7 @@ class Solution:
301257
l , r = 0, len(nums) - 1
302258

303259
while l < r:
304-
mid = (l + r) // 2
260+
mid = l + (r - l) // 2
305261
if nums[mid] > nums[r]:
306262
l = mid + 1
307263
elif nums[mid] < nums[r] or nums[mid] != nums[l]:
@@ -314,10 +270,7 @@ class Solution:
314270

315271
### [search-in-rotated-sorted-array](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/)
316272

317-
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转。
318-
> ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
319-
> 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
320-
> 你可以假设数组中不存在重复的元素。
273+
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转,例如,数组 [0, 1, 2, 4, 5, 6, 7] 可能变为 [4, 5, 6, 7, 0, 1, 2]。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1。假设数组中不存在重复的元素。
321274
322275
```Python
323276
class Solution:
@@ -326,7 +279,7 @@ class Solution:
326279
l , r = 0, len(nums) - 1
327280

328281
while l <= r:
329-
mid = (l + r) // 2
282+
mid = l + (r - l) // 2
330283
if nums[mid] == target:
331284
return mid
332285
elif nums[mid] > target:
@@ -342,15 +295,9 @@ class Solution:
342295
return -1
343296
```
344297

345-
注意点
346-
347-
> 面试时,可以直接画图进行辅助说明,空讲很容易让大家都比较蒙圈
348-
349298
### [search-in-rotated-sorted-array-ii](https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii/)
350299

351-
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转。
352-
> ( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
353-
> 编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。(包含重复元素)
300+
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转,例如,数组 [0, 0, 1, 2, 2, 5, 6] 可能变为 [2, 5, 6, 0, 0, 1, 2]。编写一个函数来判断给定的目标值是否存在于数组中,若存在返回 true,否则返回 false。数组中可能包含重复元素。
354301
355302
```Python
356303
class Solution:
@@ -363,7 +310,7 @@ class Solution:
363310
l += 1
364311
r -= 1
365312
continue
366-
mid = (l + r) // 2
313+
mid = l + (r - l) // 2
367314
if nums[mid] == target:
368315
return True
369316
elif nums[mid] > target:
@@ -379,6 +326,28 @@ class Solution:
379326
return False
380327
```
381328

329+
## 隐含的二分搜索
330+
331+
有时用到二分搜索的题目并不会直接给你一个有序数组,它隐含在题目中,需要你去发现或者构造。一类常见的隐含的二分搜索的问题是求某个有界数据的最值,以最小值为例,当数据比最小值大时都符合条件,比最小值小时都不符合条件,那么符合/不符合条件就构成了一种有序关系,再加上数据有界,我们就可以使用二分搜索来找数据的最小值。注意,数据的界一般也不会在题目中明确提示你,需要你自己去发现。
332+
333+
### [koko-eating-bananas](https://leetcode-cn.com/problems/koko-eating-bananas/)
334+
335+
```Python
336+
class Solution:
337+
def minEatingSpeed(self, piles: List[int], H: int) -> int:
338+
339+
l, r = 1, max(piles)
340+
341+
while l < r:
342+
mid = l + (r - l) // 2
343+
if sum([-pile // mid for pile in piles]) < -H:
344+
l = mid + 1
345+
else:
346+
r = mid
347+
348+
return l
349+
```
350+
382351
## 总结
383352

384353
二分搜索核心四点要素(必背&理解)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /