视频字幕
这是一个经典的二分查找算法实现。代码使用左闭右开区间[L,R)进行搜索,初始时L等于0,R等于数组长度。当找到目标值时设置标志位为真并跳出循环。关键在于理解搜索区间的定义和边界更新规则的匹配关系。
原代码采用左闭右开区间[L,R)的定义。初始时L等于0,R等于数组长度8,表示搜索区间[0,8),即索引0到7的所有元素。这种区间定义的优点是当L等于R时,区间为空,循环自然结束。mid等于L加R除以2向下取整,确保mid始终在有效范围内。
让我们通过具体例子说明为什么R等于mid减1不可以。假设查找key等于5,初始区间[0,8),mid等于3,arr[3]等于7大于key。如果错误地设置R等于mid减1等于2,新区间变成[0,2),这将排除位置2,而位置2恰好存储着我们要找的值5。正确做法是R等于mid等于3,新区间[0,3)仍包含位置2。
将mid计算公式改为L加R加1除以2会破坏算法正确性。这种向上取整的计算方式有两个严重问题:第一,可能导致数组越界,比如区间[2,3)时,mid计算为3,访问arr[3]会越界;第二,可能导致死循环,比如区间[1,3)时,如果arr[2]大于key,R更新为2,区间变成[1,2),但下次循环mid仍为2,L和R值不再改变,形成死循环。
这是一段典型的二分查找代码。我们需要分析两个关键问题:第一,能否将R等于mid改为R等于mid减1?第二,能否将mid的计算公式从L加R除以2改为L加R加1除以2?要回答这些问题,我们需要深入理解二分查找的核心机制。
首先我们来看第一个问题。原代码使用左闭右开区间,也就是方括号L逗号R右圆括号。这意味着搜索范围包含数组下标L到R减1的所有元素,而R指向搜索范围外的下一个位置。当数组中间值大于目标值时,我们知道目标值只能在左半部分,也就是从L到mid减1的范围内。因此设置R等于mid是正确的,因为这保持了左闭右开的区间性质。如果改为R等于mid减1,搜索范围就变成了左闭右闭区间,这会导致区间定义不一致,可能漏掉正确答案。
让我们通过一个具体例子来看看为什么R等于mid减1是错误的。假设数组是1、3、5、7、9,我们要查找3。初始时L等于0,R等于5,搜索范围是[0,5)。计算mid等于2,数组第2个元素是5,大于目标值3。如果错误地设置R等于mid减1等于1,新的搜索范围就变成[0,1),只包含数组第0个元素1,完全丢失了正确答案3。而正确的做法是设置R等于mid等于2,搜索范围变成[0,2),包含元素1和3,下一轮就能找到目标值。
现在我们来看第二个问题。mid的计算方式必须与区间定义保持一致。原代码使用左闭右开区间,配合向下取整的mid计算,即L加R除以2。这保证了mid始终在L到R减1的范围内。如果改为L加R加1除以2,这实际上是向上取整的计算方式,通常用于左闭右闭区间。当我们在左闭右开区间中使用向上取整时,可能会得到等于R的mid值,这会导致数组访问越界。比如当L等于2,R等于3时,向上取整得到mid等于3,但3不在[2,3)区间内,访问arr[3]可能越界。
总结一下,二分查找算法的正确性依赖于三个核心组件的完美匹配:搜索区间定义、mid计算方式和边界更新规则。原代码采用左闭右开区间、向下取整的mid计算和相应的边界更新规则,这是一套完整匹配的组合。如果随意修改其中任何一个组件,比如改为R等于mid减1或mid等于L加R加1除以2,都会破坏这种匹配关系,导致算法错误。记住,不要随意混搭不同实现方式的组件!