"我会二分"的人很多,写不出没off-by-one的很少。面试官专门用这题筛。
经典:精确查找。while(l<=r): mid=(l+r)/2; arr[mid]==t返回;小了l=mid+1,大了r=mid-1。
lower_bound:第一个>=target的下标。while(l<r): mid=(l+r)/2; arr[mid]<t时l=mid+1,否则r=mid。返回l。
upper_bound:第一个>target的下标。同lower_bound改成<=。
二分答案:答案对某谓词单调时(K个工人能否做完?)。对K二分。
旋转有序数组:mid和l(或r)比,判断哪半有序,再看target是否在那半内。
我背两个模板。"精确查找":`while(l<=r){ m=l+(r-l)/2; 相等返回m; 小了l=m+1; 大了r=m-1; }`——闭区间,l>r没找到。"第一个满足谓词":`while(l<r){ m=(l+r)/2; 满足r=m; 不满足l=m+1; }`——l==r就是答案。lower_bound就是谓词arr[m]>=target;upper_bound就是arr[m]>target。"二分答案"是同一个模板把谓词换成可行性判断——D天装运的capacity、minimize max之类。旋转数组分支:l..m或m..r哪半有序就在它范围里判target。
用`mid=l+(r-l)/2`而不是`(l+r)/2`——大下标溢出。
开闭区间先定好就一以贯之——混用是#1 bug来源。
即答侠给了两个模板+断言——粘贴改编。
不变量不同。精确查找失败时两边都缩;lower_bound里r始终是"候选答案",不能剔除。
除非搜索域上有单调性。"二分答案"就是把无序问题转化成可二分的典型招。