相关文章:
C++算法入门:二分查找 I (二分查找|在排序数组中查找元素的第一个和最后一个位置|X的平方根|搜索插入位置)
目录
1.山脉数组的峰顶索引
https://leetcode.cn/problems/peak-index-in-a-mountain-array/description/


理解题意
题目给定一个数组,这个数组的元素呈现先严格递增,后严格递减的趋势,形状像一座"山脉"。我们需要找到这个山脉的"峰顶"元素的索引。题目保证输入的数组一定是一个山脉数组,且长度至少为 3。
算法原理
算法原理因为数组具有非常明显的"二段性"(左侧单调递增,右侧单调递减),我们可以利用二分查找来寻找分界点(峰顶)。在这段代码中,我们通过比较 arr[mid] 和 arr[mid - 1] 的大小关系来确定当前 mid 所处的坡度:
1.计算 mid 时使用了向上取整 mid = left +(right- left + 1)/ 2 。
2.如果 arr[mid] < arr[mid - 1] ,说明 mid 和 mid - 1 处于下降区间(右侧坡度),峰顶一定在 mid-1 或更左侧,所以区间缩小为 [left,mid - 1] ,即 right = mid - 1 。
3.否则(arr[mid] > arr[mid -1] ),说明处于上升区间(左侧坡度) 或者mid 刚好是峰顶,峰顶一定在 mid 或更右侧,所以区间缩小为 [mid,right],即 left = mid 。
-
循环直到 left == right ,找到峰顶。
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])
{
right = mid - 1;
}
else
{
left = mid;
}
}
return left;
}
};
2.寻找峰值
https://leetcode.cn/problems/find-peak-element/


理解题意
给定一个整数数组nums,找到其中的峰值元素(严格大于左右相邻值的元素)并返回其索引。数组中可能存在多个峰值,返回其中任意一个即可。题目假设数组越界位置(nums[-1] 和nums[n])的值为负无穷小。
算法原理
虽然整个数组不具备单调性,但我们可以利用局部的单调性来使用二分查找。只要顺着递增的方向走,由于边界是负无穷,最后一定能走到一个峰值。
-
计算中间位置mid,比较 nums[mid] 和 nums[mid + 1] 。
-
如果 nums [mid] < nums[mid + 1] ,说明此时处于一个向上的局部坡度。因为最右侧边界是负无穷,继续往右走必定能遇到峰值,所以向右收缩区间:left = mid + 1 。
3.如果nums[mid] > nums[mid + 1](题目保证相邻元素不相等),说明此时处于向下的局部坡度,或者 mid 本身就是局部峰值。在 mid 及其左侧必定存在峰值,所以向左收缩区间:right = mid 。
4.循环结束时 left == right,即可找到一个峰值。
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;
else right = mid;
}
return left;
}
};
3.寻找旋转排序数组中的最小值
https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/description/


理解题意
一个原本严格升序排列的数组,在某个未知的点进行了旋转(比如[0,1,2,4,5,6,7] 变成了
4,5,6,7,0,1,2\] )。 要求在 O(log n) 的时间复杂度内找到数组中的最小值。 数组中不存在重复元素。
### 算法原理
固定nums 最右边的数,利用旋转数组的二段性:旋转后的数组可以看作由两段升序子数组组成(我们称左边部分为第一段,右边部分为第二段)。最小值显然是第二段的第一个元素。我们取最右侧元素nums\[right\] 作为基准进行比较:
1.因为第二段的所有元素都小于等于×,而第一段的所有元素都严格大于×。
2.当nums\[mid\] \> x时,说明 mid 落在了第一段,最小值肯定在 mid 的右边,因此 left= mid + 1
3.当 nums\[mid]\<= x 时,说明 mid 落在了第二段,mid 可能是最小值,也可能最小值在它的左侧,因此 right = mid 。
class Solution
{
public:
int findMin(vector