二分查找与循环不变量

二分法和循环不变量:

循环不变量的意义:

变量的意义要不变,始终要在有效的范围内,这是保证程序正确性的一种方法

循环不变量主要用来帮助我们理解算法的正确性。关于循环不变式,我们必须证明三条性质:

  1. 初始化:循环的第一次迭代之前,它为真。
  2. 保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真。
  3. 终止:在循环终止时,不变式为我们提供一个有用的性质,该性质有助于证明算法是正确的。

二分法中使用循环不变量来确定循环终止条件和区间更新式的写法

二分查找一般由三个主要部分组成:
预处理 —— 如果集合未排序,则进行排序。
二分查找 —— 使用循环或递归在每次比较后将查找空间划分为两半。
后处理 —— 在剩余空间中确定可行的候选者。

为什么二分法经常写乱?

我们写二分法时常常无法确定循环终止条件是否取等号,不知道区间更新式成right = mid还是right = mid - 1?

写二分法经常写乱,是因为区间的定义没有想清楚,二分查找的边界条件(区间)的定义就是不变量。

怎样使用不变量原则确定循环终止条件和区间更新式

在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,让循环内的区间始终满足定义的性质,这就是循环不变量规则。

区间的定义一般为两种,左闭右闭即[left, right](target一定在[left, right]内)或者左闭右开即[left, right),即target一定在[left, right)内。

我们需要在二分的初始化和保持(while循环)中使得循环终止条件和区间更新式满足:target一定在区间内。

一个力扣题目:35. Search Insert Position

import sys
class Solution:
    def searchInsert(self, nums, target: int) -> int:
        '''
        二分查找的最终结果与target的大小关系是不确定的,
        所以要加条件判断,二分还是感觉有点慌,终止条件和上下取整不确定
        '''
        left, right = 0, len(nums) - 1
        while left <= right:
            mid = (left + right) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                right = mid - 1
            elif nums[mid] < target:
                left = mid + 1
        print(left, mid, right)
        '''
        这一句带逻辑的return其实是额外的,本身不属于二分,两点需要注意
        1. 原版的二分查找不会找插入位置,而是找不到就直接返回-1,
        最后插入哪个位置要看问题的逻辑,对推出循环的区间做一点小的逻辑分析
        2. 循环中一直满足:不变量(区间)[left, right]包括了target,因此
        因此一定要不满足这个性质时才退出循环,(left==right满足性质,因此在循环内)
        '''
        return mid + 1  if nums[mid] < target else mid 
if __name__ == '__main__':
    obj = Solution()
    nums = [1,3,5,6]
    target = 5
    nums = [1,3,5,6]
    target = 2
    nums = [1,3,5,6]
    target = 4
    sys.stdout.write(str(obj.searchInsert(nums, target)))

参考:https://kelvinmao.github.io/利用循环不变式写出正确的二分查找及其衍生算法/

为什么官方的二分法的题解很多都是写的low + (high - low) // 2 而不是 (high + low) // 2

low+high在low和high特别大的时候可能会造成溢出,使用减法避免了溢出发生

posted @   wjybq  阅读(518)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示