目录
1、704. 二分查找
2、34. 在排序数组中查找元素的第一个和最后一个位置
3、69. x的平方根
4、35. 搜索插入位置
5、852. 山脉数组的峰顶索引
6、162. 寻找峰值
7、153. 寻找旋转排序数组中的最小值
8、LCR 173. 点名
1、704. 二分查找
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;elsereturn mid;}return -1;}
};
思路:
首先,定义了两个变量left和right,分别表示数组的左边界和右边界。初始时,left被设置为数组的第一个元素的索引(0),right被设置为数组的最后一个元素的索引(nums.size() - 1)。
然后,使用一个循环来执行二分查找。循环条件是left小于等于right,这表示还存在未查找的区间。在二分查找算法中,使用left <= right作为循环条件是为了处理以下两种情况:
-
当查找区间只剩下一个元素时,即
left和right指向同一个位置时,仍然需要进行一次比较。如果使用left < right作为循环条件,那么在这种情况下循环会提前结束,导致无法正确判断目标值是否存在。 -
当查找区间为空时,即
left大于right时,循环条件为假,可以直接退出循环。这种情况下,表示目标值不存在于数组中。
在每次循环中,首先计算中间元素的索引mid,通过将左边界和右边界与左边界之差相加除以2得到。这样可以将查找区间缩小一半,并且不会超出整形范围。
接下来,通过比较中间元素nums[mid]和目标值target的大小关系,来确定下一步的查找方向。
- 如果
nums[mid]小于target,说明目标值在右半部分,将left更新为mid + 1,缩小查找区间为右半部分。 - 如果
nums[mid]大于target,说明目标值在左半部分,将right更新为mid - 1,缩小查找区间为左半部分。 - 如果
nums[mid]等于target,说明找到了目标值,直接返回mid作为结果。
如果循环结束时仍然没有找到目标值,即left大于right,则返回-1表示未找到。
2、34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {if (nums.size() == 0)return {-1, -1};int left = 0, right = nums.size() - 1;int begin = 0;// 使用二分查找找到目标值的最左边界while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] < target)left = mid + 1;elseright = mid;}// 检查最左边界的元素是否为目标值if (nums[left] != target)return {-1, -1};elsebegin = left;left = 0, right = nums.size() - 1;// 使用二分查找找到目标值的最右边界while (left < right) {int mid = left + (right - left + 1) / 2;if (nums[mid] <= target)left = mid;elseright = mid - 1;}return {begin, right};}
};
思路:寻找左右边界
首先,检查数组是否为空,如果为空,则直接返回{-1, -1}表示未找到目标值。
初始化左边界left为0,右边界right为数组长度减1。
使用二分查找来找到目标值的最左边界。循环条件是left < right。
- 在每次循环中,计算中间元素的索引
mid。 - 比较中间元素
nums[mid]与目标值target的大小关系:- 如果
nums[mid]小于目标值,说明目标值在右半部分,将left更新为mid + 1,缩小查找区间为右半部分。 - 如果
nums[mid]大于等于目标值,说明目标值在左半部分,将right更新为mid,缩小查找区间为左半部分。
- 如果
- 检查最左边界的元素是否为目标值。如果不是目标值,则表示未找到目标值,直接返回{-1, -1}。
将最左边界left赋值给begin,作为目标值的起始位置。
重新初始化left和right,继续使用二分查找来找到目标值的最右边界。循环条件是left < right。
- 在每次循环中,计算中间元素的索引
mid,。 - 比较中间元素
nums[mid]与目标值target的大小关系:- 如果
nums[mid]小于等于目标值,将left更新为mid,缩小查找区间为右半部分。 - 如果
nums[mid]大于目标值,将right更新为mid - 1,缩小查找区间为左半部分。
- 如果
- 返回结果为{begin, right},其中
begin为目标值的最左边界,right为目标值的最右边界。
3、69. x的平方根
思路:寻找左右边界
首先,检查输入的数x是否小于1,如果是,则直接返回0。
然后,定义了两个变量left和right,分别表示查找区间的左边界和右边界。初始时,left被设置为1,right被设置为x。
接下来,使用二分查找来逼近平方根的值。循环条件是left < right,这表示还存在未查找的区间。
在每次循环中,首先计算中间元素的值mid,通过将左边界和右边界相加除以2得到。这里使用了(right - left + 1) / 2来确保向上取整,以保证查找区间向上移动。
然后,通过比较中间元素的平方mid * mid与目标值x的大小关系,来确定下一步的查找方向。
- 如果
mid * mid小于等于x,说明平方根在右半部分,将left更新为mid,缩小查找区间为右半部分。 - 如果
mid * mid大于x,说明平方根在左半部分,将right更新为mid - 1,缩小查找区间为左半部分。
最后,循环结束时,left和right相等,表示找到了平方根的整数部分。返回left作为结果。
class Solution {
public:int mySqrt(int x) {if (x < 1)return 0;int left = 1, right = x;while (left < right) {long long mid = left + (right - left + 1) / 2;if (mid * mid <= x)left = mid;elseright = mid - 1;}return left;}
};
4、35. 搜索插入位置
思路:寻找左边界
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;elseright = mid;}if (nums[left] < target)return right + 1;elsereturn right;}
};
思路:寻找右边界
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 + 1) / 2;if (nums[mid] <= target)left = mid;elseright = mid - 1;}if (nums[left] < target)return right + 1;elsereturn right;}
};
5、852. 山脉数组的峰顶索引

思路:寻找左边界
class Solution {
public:int peakIndexInMountainArray(vector<int>& arr) {int left = 1, right = arr.size() - 2;while (left < right) {int mid = left + (right - left) / 2;if (arr[mid] < arr[mid + 1])left = mid + 1;elseright = mid;}return left;}
};
思路:寻找右边界
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;elseright = mid - 1;}return left;}
};
两个解决方案都是有效的,并且都具有 O(log(n)) 的时间复杂度。它们的区别在于二分查找的条件判断。
- 第一个解决方案中,二分查找的条件判断是
arr[mid] > arr[mid - 1],即判断当前位置的元素是否大于前一个元素。如果满足这个条件,则说明峰值在当前位置或右侧,将left更新为mid;否则,峰值在左侧,将right更新为mid - 1。 - 第二个解决方案中,二分查找的条件判断是
arr[mid] < arr[mid + 1],即判断当前位置的元素是否小于后一个元素。如果满足这个条件,则说明峰值在右侧,将left更新为mid + 1;否则,峰值在左侧或当前位置,将right更新为mid。
6、162. 寻找峰值

思路:寻找左边界
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])left = mid + 1;elseright = mid;}return left;}
};
思路:寻找右边界
class Solution {
public:int findPeakElement(vector<int>& nums) {int left = 0, right = nums.size() - 1;while (left < right) {int mid = left + (right - left + 1) / 2;if (nums[mid] > nums[mid - 1])left = mid;elseright = mid - 1;}return left;}
};

7、153. 寻找旋转排序数组中的最小值

思路:以最右端点为基准点进行比较,逐渐缩小范围。
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;elseright = mid;}return nums[left];}
};

8、LCR 173. 点名

思路:根据数组元素与下标相等和不相等划分两个区间,最后,如果元素与其下标不相等,则返回其下标即为所缺元素;如果元素与其下标相等,则所缺元素为数组最大元素加一,即为left或right加一。
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;elseright = mid;}return left == records[left] ? left + 1 : left;}
};
