二分查找
目录
试题一:二分查找

算法原理
解法一:暴力枚举
从左往右遍历一遍数组
时间复杂度:O(N)
解法二:二分查找算法
利用二段性,实现二分查找
循环结束的条件为left > right

二分查找算法的效率:O(logN)


朴素二分模板

代码编写
- 个人版本
cpp
class Solution
{
public:
int search(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1;
while(left <= right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target)
{
left = mid + 1;
}
else if(nums[mid] > target)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
};
- 标准版本
cpp
class Solution
{
public:
int search(vector<int>& nums, int target)
{
int left = 0,right = nums.size() - 1;
while(left <= right)
{
int mid = left + (right - left) / 2;//防止溢出
if(nums[mid] < target) left = mid + 1;
else if(nums[mid] > target) right = mid - 1;
else return mid;
}
return -1;
}
};
试题2:在排序数组中查找元素第一个和最后一个位置

算法原理
解法一:暴力查找
从左往右遍历
时间复杂度:O(N)

解法二:二分算法
朴素二分只能找到这个数值,但是并不知道起始位置与终止位置
所以需要利用二段性进行优化
查找区间的左端点

查找区间的右端点

代码编写
- 个人版本
cpp
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
vector<int> ret;
if (nums.size() == 0)
{
ret = {-1, -1};
return ret;
}
//找左边界
int first_left = 0;
int first_right = nums.size() - 1;
while (first_left < first_right)
{
int first_mid = first_left + (first_right - first_left) / 2;
if (nums[first_mid] < target)
{
first_left = first_mid + 1;
}
else
{
first_right = first_mid;
}
}
//判断target是否不存在
if (nums[first_left] != target)
{
ret = {-1, -1};
return ret;
}
//找右边界
int last_left = 0;
int last_right = nums.size() - 1;
while (last_left < last_right)
{
int last_mid = last_left + (last_right - last_left + 1) / 2;
if (nums[last_mid] > target)
{
last_right = last_mid - 1;
}
else
{
last_left = last_mid;
}
}
ret = {first_left, last_left};
return ret;
}
};
- 标准版本
cpp
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
//处理边界情况
if(nums.size() == 0) return {-1,-1};
int begin = 0;
//#1:二分左端点
int left = 0,right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target)
{
left = mid + 1;
}
else
{
right = mid;
}
}
//判断是否有结果
if(nums[left] != target) return{-1,-1};
else begin = left;//标记左端点
//#2:二分右端点
left = 0,right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(nums[mid] <= target) left = mid;
else right = mid - 1;
}
return {begin,right};
}
};
试题3:搜索插入位置

算法原理
查找区间左端点

代码编写
- 个人版本
cpp
class Solution
{
public:
int searchInsert(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target)
{
left = mid + 1;
}
else
{
right = mid;
}
}
if(target > nums[nums.size() - 1])
{
return nums.size();
}
return left;
}
};
- 标准版本
cpp
class Solution
{
public:
int searchInsert(vector<int>& nums, int target)
{
int left = 0,right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target) left = mid + 1;
else right = mid;
}
if(nums[left] < target) return left + 1;
return left;
}
};
试题4:x的平方根

算法原理
查找区间右端点

代码编写
- 个人版本
cpp
class Solution
{
public:
int mySqrt(int x)
{
if(x == 0)
{
return 0;
}
long long right = x;
long long left = 0;
while(left < right)
{
long long mid = left + (right - left + 1) / 2;
if(mid * mid > x)
{
right = mid - 1;
}
else
{
left = mid;
}
}
return right;
}
};
- 标准版本
cpp
class Solution
{
public:
int mySqrt(int x)
{
if(x < 1) return 0;//处理边界情况
long long left = 1,right = x;
while(left < right)
{
long long mid = left + (right - left + 1) / 2;//防溢出
if(mid * mid <= x) left = mid;
else right = mid - 1;
}
return left;
}
};
试题5:山峰数组的峰顶

算法原理
解法一:暴力枚举
时间复杂度:O(N)
解法二:二分查找
利用二段性,查找区间右端点

代码编写
- 个人版本
cpp
class Solution
{
public:
int peakIndexInMountainArray(vector<int>& arr)
{
int left = 0;
int right = arr.size() - 1;
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(arr[mid] > arr[mid - 1])
{
left = mid;
}
else
{
right = mid - 1;
}
}
return left;
}
};
- 标准版本
cpp
class Solution
{
public:
int peakIndexInMountainArray(vector<int>& arr)
{
int left = 1,right = arr.size() - 2;
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(arr[mid] > arr[mid - 1]) left = mid;
else right = mid - 1;
}
return left;
}
};
试题6:寻找峰值

算法原理
解法一:暴力解法
从第一个位置一直往后走,分情况讨论
时间复杂度:O(N)

解法二:二分查找
在严格无序的情况下,如果发现二段性,依旧能够使用二分算法

代码编写
- 个人版本
cpp
class Solution
{
public:
int findPeakElement(vector<int>& nums)
{
int left = 0;
int right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] > nums[mid + 1])
{
right = mid;
}
else
{
left = mid + 1;
}
}
return left;
}
};
- 标准版本
cpp
class Solution
{
public:
int findPeakElement(vector<int>& nums)
{
int left = 0,right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] > nums[mid + 1]) right = mid;
else left = mid + 1;
}
return left;
}
};
试题7:寻找旋转排序数组中的最小值

算法原理
解法一:暴力查找最小值
时间复杂度:O(N)
解法二:二分查找算法

代码编写
- 个人版本
cpp
class Solution
{
public:
int findMin(vector<int>& nums)
{
int left = 0;
int right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] > nums[nums.size() - 1])
{
left = mid + 1;
}
else
{
right = mid;
}
}
return nums[left];
}
};
- 标准版本
cpp
class Solution
{
public:
int findMin(vector<int>& nums)
{
int left = 0,right = nums.size() - 1;
int x = nums[right];
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] > x) left = mid + 1;
else right = mid;
}
return nums[left];
}
};
试题8:点名

算法原理
O(N)解法:
哈希表
直接遍历
位运算
高斯求和
O(logN)解法:二分查找

代码编写
- 个人版本
cpp
class Solution
{
public:
int takeAttendance(vector<int>& records)
{
int left = 0;
int right = records.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(records[mid] == mid)
{
left = mid + 1;
}
else
{
right = mid;
}
}
return records[left] == left ? records.size() : left;
}
};
- 标准版本
cpp
class Solution
{
public:
int takeAttendance(vector<int>& records)
{
int left = 0,right = records.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(records[mid] == mid) left = mid + 1;
else right = mid;
}
return records[left] == left ? left + 1 : left;
}
};