# 6. 一些细节(以问答形式呈现)

# 细节一:为什么有些时候取 mid 的时候需要上取整?

回答:是否需要上取整,只和区间划分的逻辑有关。如果不调整,会出现死循环。

image.png

初次接触的时候,很多朋友觉得这件事情非常难以理解,我们的建议是:一旦遇到死循环,可以在程序中输出 leftrightmid 的值看一下就很清楚了。「力扣」第 69 题:x 的平方根 (opens new window)题解 (opens new window) ,我们演示了如何打印变量、观察到死循环。

结论:当区间只剩下两个元素的时候,left = midright = mid - 1 这种划分方式,如果 mid 使用默认下取整的方式,在数值上 left = mid,而它对应的其中一个区间是 [mid..right],在这种情况下,下一轮搜索区间还是 [left..right],搜索区间没有减少,会进入死循环。

提示:「看到边界设置的代码是 left = mid 时,需要把 mid 的取法调整为上取整,以避免死循环」,这件事情也完全不用记忆,题目做得多了,自然而然就记住了。还是我们在题解中和大家多次强调的一件事情:遇到代码出错的时候,一定要耐心调试,把变量打印出来看一下,是最好的学习的方法

# 细节二:有些学习资料上说 while (left < right) 表示区间是 [left..rihgt) ,为什么你这里是 [left..rihgt]

回答:区间的右端点到底是开还是闭,完全由编写代码的人决定,不需要固定。只要编码的人逻辑前后一致,并且保持不变(这件事情叫遵守「循环不变量」),就是正确的。

我们通篇讲的都是 左闭右闭 区间,理由是这样定义更直接。

# 细节三:有些学习资料给出了三种模板,例如「力扣」推出的 LeetBook 之 「二分查找专题 (opens new window)」,应该如何看待它们?

回答:我在学习的时候,LeetBook 也是我重要的学习材料之一。三种模板其实区别仅在于退出循环的时候,区间 [left..right] 里有几个数。

  • while (left <= right) :退出循环的时候,right 在左,left 在右,区间为空区间,所以要讨论返回 leftright
  • while (left < right) :退出循环的时候,leftright 重合,区间里只有一个元素,这一点是我们很喜欢的;
  • while (left + 1< right) :退出循环的时候,left 在左,right 在右,区间里有 2 个元素,需要编写专门的逻辑。这种写法在设置 leftright 的时候不需要加 1 和减 1。看似简化了思考的难度,但实际上屏蔽了我们应该且完全可以分析清楚的细节。退出循环以后一定需要编写专门的逻辑,讨论返回哪一个,也增加了编码的难度。

我个人的经验是:完全不用第三个,理由是不会使得问题变得更简单,反而很累赘。

我常用头两个,第一个在简单问题中用,第二个在复杂问题中用。在题解的第 5 部分「5. 两种写法对比」的「重要经验」里也叙述了用它们的理由和场景。