时间复杂度O(logn)
二分查找必须先要给序列排序,也就是说要使用二分查找算法,序列必须是有序的。
1.普通的二分查找 LeetCode 704. 二分查找
class Solution { public: int search(vector<int>& nums, int target) { int l = 0, r = nums.size()-1; while(l <= r) { int mid = (l + r) / 2; if(nums[mid] == target) return mid; else if(nums[mid] > target) r = mid - 1; else l = mid + 1; } return -1; } };递归的写法:
class Solution { public: int _search(vector<int>& nums, int left, int right, int target) { if(left > right) return -1; int mid = ((right - left) >> 1) + left;//"+"比">>"优先级高 if(nums[mid] == target) return mid; else if(nums[mid] > target) return _search(nums, left, mid-1, target); else return _search(nums, mid+1, right, target); } int search(vector<int>& nums, int target) { return _search(nums, 0, nums.size()-1, target); } };注意:int mid = (l + r) / 2;可以优化为:int mid = (r - l) / 2 + l;//防止l+r越界。除以2也可优化为按位右移:int mid =((r - l) / >>1) + l
2.二分查找的变种问题:找第一个目标元素或者找最后一个目标元素,比如:1,2,3,3,4,5 target=3,第一个3就是返回下标2,最后一个就是返回下标3.
LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置
class Solution { public: int searchLeft(vector<int>& nums, int target) { int l = 0, r = nums.size()-1; while(l <= r) { int mid = ((r - l) >> 1) + l; if(nums[mid] > target) r = mid - 1; else if(nums[mid] < target) l = mid + 1; else{ if(mid == 0 || nums[mid-1] != target) return mid;//注意mid与可能到第一个元素,那么nums[mid+1]就会数组越界 else r = mid - 1; } } return -1; } int searchRight(vector<int>& nums, int target) { int l = 0, r = nums.size()-1; while(l <= r) { int mid = ((r - l) >> 1) + l; if(nums[mid] > target) r = mid - 1; else if(nums[mid] < target) l = mid + 1; else{ if(mid == nums.size()-1 || nums[mid+1] != target) return mid;//注意mid与可能到最后一个元素,那么nums[mid+1]就会数组越界 else l = mid + 1; } } return -1; } vector<int> searchRange(vector<int>& nums, int target) { vector<int> res; int left = searchLeft(nums, target); int right = searchRight(nums, target); res.push_back(left); res.push_back(right); return res; } };3.求x的平方根 LeetCode 69. x 的平方根
class Solution { public: int mySqrt(int x) { if(x <= 1 ) return x; int left = 0, right = x, ret; while(left <= right) { int mid = (right - left) / 2 + left; int target = x / mid; if(mid == target )//target = x / mid;此题可转换为查找最后一个不大于目标值的元素 return mid; else if( target < mid)//目标值小于选定值,所以区间要向左边收缩 { right = mid - 1; } else { left = mid + 1; ret = mid;//比如6,当mid=2时,真正的结果在2-3之间,但结果取2 } } return ret; } };4.二分查找变种:
(1)查找第一个大于等于目标元素(1,2,4,6 target=3,结果返回4);
int search(vector<int>& nums, int target) { int l = 0, r = nums.size()-1; while(l <= r) { int mid = ((r - l) >> 1) + l; if(nums[mid] >= target) { if(mid == 0 || nums[mid-1] <target) return mid; else r = mid - 1; } else l = mid + 1; } return -1; }(2)查找最后一个小于给定元素(1,2,4,6 target=3,结果返回2。
int search(vector<int>& nums, int target) { int l = 0, r = nums.size()-1; while(l <= r) { int mid = ((r - l) >> 1) + l; if(nums[mid] <= target) { if(mid == 0 || nums[mid+1] > target) return mid; else l = mid + 1; } else r = mid - 1; } return -1; }