@@ -382,47 +382,19 @@ function binarySearchRight(nums, target) {
382382
383383#### 思维框架
384384
385- 不管是寻找最左插入位置还是后面的寻找最右插入位置,我们的更新指针代码都是一样的。即:
386-
387- ```
388- l = mid + 1
389- # or
390- r = mid
391- ```
392-
393- > 当然也有别的方式(比如 mid 不是向下取整,而是向上取整), 但是这样可以最小化记忆成本。
394-
395- 有的人有疑问,这样设置结束条件会不会漏过正确的解,必然是不会的,如果更新区间代码是 r = mid - 1 还有可能错过,而 r = mid 这种代码根本就没有摒弃 r。
396-
397- 这样描述可能不够清晰。为了探究这个问题, 我们继续往下看。
398-
399- 以 nums = [ 1,3,4] , target = 2 为例。我们要找的其实就是 [ 1,x,3,4] 中 x 所在的位置,换句话说** 我们的目的就是不断收缩搜索区间到 x 位置** 。
400-
401- - 如果 nums[ mid] < x,由于 nums 是单调增的,因此 mid 一定在 x 左侧,小于等于 mid 部分一定不是最终解,我们将 mid + 1 作为新的搜索区间的左区间。
402- - 如果 nums[ mid] >= x,由于 nums 是单调增的,因此 mid 一定在 x 的位置或者在 x 的右侧,由于 mid 可能就在 x 的位置,所以 r = mid - 1 就可能错过了这种解,使用 r = mid 这种更新方式可避免这种情况。
403-
404- 说得再直白一点。假设使用最左插入位置的方式查找的索引 为 i,那么应该满足不等式 nums[ i] >= target。
405-
406- - nums = [ 1,3,4] , target = 2 的情况 i = 1,nums[ i] = 3 > 2
407- - nums = [ 1,2,2,2,3,4] , target = 2 的情况 i = 1,nums[ i] = 2 = 2
408-
409- 因此
410-
411- - 如果 nums[ mid] 已经小于 target 了,就必然是不可能的情况,直接通过 l = mid + 1 排除
412- - 否则无法排除, 使用 r = mid 即可, mid 就是我们找到的一个备胎。
385+ 如果你将** 寻找最左插入位置** 看成是** 寻找最左满足** 大于等于 x 的值,那就可以和前面的知识产生联系,使得代码更加统一。唯一的区别点在于** 前面是最左满足等于 x** ,这里是** 最左满足大于等于 x** 。
413386
414387具体算法:
415388
416389- 首先定义搜索区间为 [ left, right] ,注意是左右都闭合,之后会用到这个点。
417390
418391> 你可以定义别的搜索区间形式,不过后面的代码也相应要调整,感兴趣的可以试试别的搜索区间。
419392
420- - 由于我们定义的搜索区间为 [ left, right] ,因此当 left <= right 的时候,搜索区间都不为空。 但由于上面提到了更新条件有一个 ` r = mid ` ,因此如果结束条件是 left <= right 则会死循环。因此结束条件是 left < right。
393+ - 由于我们定义的搜索区间为 [ left, right] ,因此当 left <= right 的时候,搜索区间都不为空。 也就是说我们的终止搜索条件为 left <= right。
421394
422- - 循环体内,我们不断计算 mid ,并将 nums[ mid] 与 目标值比对。
423- - 如果 nums[ mid] 大于等于目标值(mid 在 x 位置或者 x 的右侧), r 也可能是目标解(等于的情况),r - 1 可能会错过解,因此我们使用 r = mid。
424- - 如果 nums[ mid] 小于目标值(mid 在 x 的左侧), mid 以及 mid 左侧都不可能是解,至少是 mid + 1 才行,因此我们使用 l = mid + 1。
425- - 最后直接返回 l 或者 r 即可。(并且不需要像` 最左满足条件的值 ` 那样判断了)
395+ - 当 A[ mid] >= x,说明找到一个备胎,我们令 r = mid - 1 将 mid 从搜索区间排除,继续看看有没有更好的备胎。
396+ - 当 A[ mid] < x,说明 mid 根本就不是答案,直接更新 l = mid + 1,从而将 mid 从搜索区间排除。
397+ - 最后搜索区间的 l 就是最好的备胎,备胎转正。
426398
427399#### 代码模板
428400
@@ -433,14 +405,11 @@ def bisect_left(nums, x):
433405 # 内置 api
434406 bisect.bisect_left(nums, x)
435407 # 手写
436- l, r = 0 , len (nums ) - 1
437- while l < r:
408+ l, r = 0 , len (A ) - 1
409+ while l <= r:
438410 mid = (l + r) // 2
439- if nums[mid] < x:
440- l = mid + 1
441- else :
442- r = mid
443- # 由于 l 和 r 相等,因此返回谁都无所谓。
411+ if A[mid] >= x: r = mid - 1
412+ else : l = mid + 1
444413 return l
445414```
446415
@@ -450,22 +419,19 @@ def bisect_left(nums, x):
450419
451420#### 思维框架
452421
453- 和 ` 寻找最左插入位置 ` 类似。不同的地方在于:如果有多个满足条件的值,我们返回最右侧的。 比如一个数组 nums: [ 1,2,2,2,3,4 ] ,target 是 2,我们应该插入的位置是 4 。
422+ 如果你将 ** 寻找最右插入位置 ** 看成是 ** 寻找最右满足 ** 大于 x 的值,那就可以和前面的知识产生联系,使得代码更加统一。唯一的区别点在于 ** 前面是最左满足等于 x ** ,这里是 ** 最左满足大于 x ** 。
454423
455424具体算法:
456425
457426- 首先定义搜索区间为 [ left, right] ,注意是左右都闭合,之后会用到这个点。
458427
459428> 你可以定义别的搜索区间形式,不过后面的代码也相应要调整,感兴趣的可以试试别的搜索区间。
460429
461- - 由于我们定义的搜索区间为 [ left, right] ,因此当 left <= right 的时候,搜索区间都不为空。 但由于上面提到了更新条件有一个` r = mid ` ,因此如果结束条件是 left <= right 则会死循环。因此结束条件是 left < right。
462-
463- > 有的人有疑问,这样设置结束条件会不会漏过正确的解,其实不会。举个例子容易明白一点。 比如对于区间 [ 4,4] ,其包含了一个元素 4,搜索区间不为空。如果我们的答案恰好是 4,会被错过么?不会,因为我们直接返回了 4。
430+ - 由于我们定义的搜索区间为 [ left, right] ,因此当 left <= right 的时候,搜索区间都不为空。 也就是说我们的终止搜索条件为 left <= right。
464431
465- - 循环体内,我们不断计算 mid ,并将 nums[ mid] 与 目标值比对。
466- - 如果 nums[ mid] 小于等于目标值, mid 以及 mid 左侧都不可能是解,因此我们使用 l = mid + 1。
467- - 如果 nums[ mid] 大于目标值, r 也可能是目标解,r - 1 可能会错过解,因此我们使用 r = mid。
468- - 最后直接返回 l 或者 r 即可。(并且不需要像` 最左满足条件的值 ` 那样判断了)
432+ - 当 A[ mid] > x,说明找到一个备胎,我们令 r = mid - 1 将 mid 从搜索区间排除,继续看看有没有更好的备胎。
433+ - 当 A[ mid] <= x,说明 mid 根本就不是答案,直接更新 l = mid + 1,从而将 mid 从搜索区间排除。
434+ - 最后搜索区间的 l 就是最好的备胎,备胎转正。
469435
470436#### 代码模板
471437
@@ -477,14 +443,11 @@ def bisect_right(nums, x):
477443 # 内置 api
478444 bisect.bisect_right(nums, x)
479445 # 手写
480- l, r = 0 , len (nums ) - 1
481- while l < r:
446+ l, r = 0 , len (A ) - 1
447+ while l <= r:
482448 mid = (l + r) // 2
483- if nums[mid] > x:
484- r = mid
485- else :
486- l = mid + 1
487- # 由于 l 和 r 相等,因此返回谁都无所谓。
449+ if A[mid] <= x: l = mid + 1
450+ else : r = mid - 1
488451 return l
489452```
490453
0 commit comments