欢迎光临散文网 会员登陆 & 注册

二分

2023-01-17 23:08 作者:raft0065  | 我要投稿

二分搜索 Binary Search

视频一:二分查找,一个视频搞定!【基础算法精讲 04】

视频二:搜索旋转排序数组【基础算法精讲 05】

    关于有序数组中的二分查找及其衍生题,我一直觉得麻烦的不是思想,而是细节。leetcode比赛分数上不去,和OJ大佬相比,不是题目做少的问题,而是缺少沉下心来反思和总结。这两天刚好看到灵神的基础算法系列视频,想着最近也没啥事,反刍一下顺便写个笔记。

    二分的模版一般有三种:有全闭区间的[left, right],左闭右开的[left, right),还有全开区间的(left, right)。每一种特点不同,循环里判断条件也不同,很看个人口味。我比较懒,觉得模版太多比较烦,也记不住,第二种最通用,所以我比较喜欢,以后碰到相关的题基本都按照这个套路写。力扣33题是一道使用二分的基础题,这里写了之后配合灵神的讲解视频,记录下几个比较有用的点:

    - 要点一:

        转化,>, <, <= 啥的都可以转化为 >=,比如找数组中 >x 的元素开始位置:lower_bound(a, target+1);找 <x  的元素结束位置:lower_bound(a, target) -1;找 <=x 的元素结束位置:lower_bound(a, target+1) -1;

    - 要点二:

        二是用左开右闭的时候一般都要后处理,因为可能找不到啥的,这里循环结束时肯定是left==right,所以到底选left还是right也不用为难了,随便拉一个判定下答案符不符合要求就行了,少了麻烦

    - 要点三:

        二分要是一般用java/c++/golang啥的可能在取mid时候会溢出,可以这样写 mid = left + (right-left)/2,但我刷题一般用python哈哈哈

    这道题我也是用[left, right)左闭右开写的,因为这道题肯定有答案所以不用后置处理。而且这里题目里数全是不同的,所以已经很方便了,不需要额外多处理。这里 nums[mid] < nums[right] 写成 nums[mid] < nums[-1] 也是可以的,反正就是通过对比mid处的数和另一个数,这个数只要能够帮助二分就行。

    这题是153的进阶,多了一个数组中的数字可以重复,所以最坏情况下退化为线性搜索,没了O(log n)的效率,不过这不是啥问题。

    - 要点一

        注意这里因为数字可以重复,所以要是找原数组最右边的数字nums[-1]比较不行了,因为当 nums[mid] == nums[-1] 时,需要去掉mid和right指向的两个重复数字中的一个(因为保留一个就等于也保留了这个数是答案的可能),这里用脚想也知道不能去掉mid,所以只能去掉right指向的那个数,不然循环就推进不下去了。而right一变,要是下次比较还和nums[-1]比,那就错了。


    这里主要是多了个mid和mid+1两个数的比较,一般题目就是用到mid,所以用到mid+1时可能会溢出,这里把right在初始化的时候-1,这样的话,之要进了while循环,里面的mid和mid+1就肯定是合法的,不用担心下标非法。至于本题答案是肯定存在的,所以不需要后处理。

    这题吧,两次二分和一次二分都行。不过感觉灵神这种一次二分的处理更好更有意思,之后把二分判定逻辑写成函数肯定更通用,所以更偏向于第二种解。另外视频里灵神是用全开区间,即 (left, right),所以初始化和中间指针的时候会和我的不一样,不过我还是喜欢我自己这种,无所谓了。

总结:

    二分感觉还是挺搞的,细节比较多,比较烦,但这么一梳理,感觉还行,以后就用左闭右开模版了 [left, right)。希望下次遇到相似的题不再怵了。


二分的评论 (共 条)

分享到微博请遵守国家法律