二分模板:
模板1
当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1,计算mid时不需要加11,即mid = (l + r)/2。
1 2 3 4 5 6 7 8 9 10 11
| int bsearch_1(int l, int r) { while (l < r) { int mid = (l + r)/2; if (check(mid)) r = mid; else l = mid + 1; } return l; }
|
模板2
当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid,此时为了防止死循环,计算mid时需要加1,即mid = ( l + r + 1 ) /2。
1 2 3 4 5 6 7 8 9 10 11
| int bsearch_2(int l, int r) { while (l < r) { int mid = ( l + r + 1 ) /2; if (check(mid)) l = mid; else r = mid - 1; } return l; }
|
为什么两个二分模板mid取值不同?
在查找的过程中,如果r更新到r = l + 1
时,模板一中mid = l + l + 1 / 2 = l
则l = mid = l,r还是l + 1,mid还是等于l不变,陷入死循环。所以模板二中多加1就是mid向上取整。但模板一中l = mid + 1,就不会有这种困扰
什么时候用模板1?什么时候用模板2?
主要是每次更新左边界时,是希望代码更新成l = mid,就mid要向上取整,如果l = mid + 1,就不用向上取整。
- 二分查找时,首先要确定我们要查找的边界值,保证每次二分缩小区间时,边界值始终包含在内。然后再选择使用什么代码来写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Solution { public: int search(vector<int>& nums, int target) { if(nums.empty()) return 0; int l = 0, r = nums.size() - 1; while(l < r) { int mid = (l + r ) / 2; if(nums[mid] >= target) { r = mid; } else l = mid + 1; } if(nums[r] != target) return 0; int left = r; l = 0, r = nums.size() - 1; while(l < r) { int mid = (l + r + 1) / 2; if(nums[mid] <= target) { l = mid; } else r = mid - 1; } int right = r; return right - left + 1; } };
|