目录
前言:
本文的主题是二分查找,通过两道题目讲解,一道是x的平方根,一道是山脉数组的封顶索引。
链接分别为:
69. x 的平方根 - 力扣(LeetCode)
852. 山脉数组的峰顶索引 - 力扣(LeetCode)
题目分为三个部分讲解,一是题目解析,二是算法原理,三是算法编写,那么,话不多说,直接进行主题咯。
x的平方根
题目解析
由题目的要求,我们需要求一个数的算法平方根,并且舍去小数部分,并且禁止我们使用pow函数等。说白了就是要我们自己模拟实现一个函数。
那么这道题的暴力解法就很简单了,将题目示例的所有平方列举出来,和题目给的x一个一个比较就可以了。当比较到x 小于某个数的平方,那么我们就可以得到x等于某个数减1.
但是这样做的话,时间复杂度就是O(N),并且没有利用数据是升序的特点。有人问了就,升序是哪里来的,我们从1开始列举平方,也就是一个升序的过程。
那么直接进入算法原理部分吧。
算法原理
我们利用二段性,就可以将数分为小于等于x和大于x的,那么我们的目标区域是小于等于x的右端点,所以我们使用模板是朴素模板呢?还是非朴素模板呢?肯定是非朴素求左端点的模板了。
所以left < right是必要的,但因为ans区域是在左边,所以left = mid,right = mid - 1,因为有了减法,所以求中间是 + 1,这些都是二分查找(2)的。
因为有了二分(2)的基础,所以我们直接进入算法编写部分。
算法编写
class Solution
{
public:
int mySqrt(int x)
{
if(x <= 0) return 0;
int 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;
}
};
这里有两个小点需要注意的,是x可能为0,所以直接返回0即可,并且因为mid * mid,可能导致数据溢出的风险,所以需要long long。
山脉数组的封顶索引
题目解析
题目要求是让我们找到封顶的最高值,并且数组的大小是从高到低,所以暴力解法非常非常简单,如果判断了后面的值小于该值,那么该值就是我们要的值。
肯定是一个O(N)就能够解决的事儿,但是题目要求我们必须找到对应logN的解决方案,那么看都不用看,直接就是二分查找。
小细节就是,左右端点肯定不可能是结果,所以我们不妨让left从1开始,right从nums.size() - 2的值开始。
算法原理
对于算法原理来说,我们无非是要找到对应的二段性,所以峰值左边就是arr[i] > arr[i - 1],右边同理可得,二段性就是从峰值开始分开的。
由题目解析我们可以知道left和right的取值,并且因为我们见数组分成了这样,那么答案是在左边,所以我们使用的二分模板肯定是非朴素的求左边的二分模板了。
还是那句话,因为有了二分查找(2)的基础,所以我们可以直接套用模板了,就不会重新讲解一下原理了。
算法编写
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;
}
};
就,非常简单~
感谢阅读!