《LeetCode之每日一题》:151.寻找峰值
Posted 是七喜呀!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《LeetCode之每日一题》:151.寻找峰值相关的知识,希望对你有一定的参考价值。
有关题目
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
示例 1:
输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
示例 2:
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
提示:
1 <= nums.length <= 1000
-2^31 <= nums[i] <= 2^31 - 1
对于所有有效的 i 都有 nums[i] != nums[i + 1]
题解
Tips
两个 pair 类型数据可以直接使用 ==,!=,<,<=,>,>= 比较大小,
比较规则是先以 first 的大小作为标准,
只有当 first 相等时才去判别 second 的大小。
法一:一次遍历(O(n))
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int n = nums.size();
int ans = 0;
if (n == 1) return 0;//[1]
if (nums[0] > nums[1]) return 0;
if (nums[n - 2] < nums[n - 1]) return n - 1;
for (int i = 1; i < n - 1; ++i){
if (nums[i - 1] < nums[i] && nums[i] > nums[i + 1]){
ans = i;
break;
}
}
return ans;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
法二:寻找最大值
class Solution {
public:
int findPeakElement(vector<int>& nums) {
return max_element(begin(nums), end(nums)) - begin(nums);
}
};
时间复杂度:O(n),n为nums长度
空间复杂度:O(1)
法三:迭代爬坡
参考官方题解
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int n = nums.size();
int idx = rand() % n;
//辅助函数, 输入下标 i,返回pair类型(0 / 1, nums[i])
//巧妙地处理了i = 0, 与i = n的状态
//对于下标0, n,限制pair->first为 0,而合法的下标为1,保证满足题干条件nums[-1] = nums[n] = -∞,在数组元素范围内数组中每个元素都是大于它们俩的
auto get = [&](int i)->pair<int, int>{
if (i == -1 || i == n){
return {0, 0};
}
return {1, nums[i]};
};
while(!(get(idx - 1) < get(idx) && get(idx) > get(idx + 1))){
if (get(idx - 1) > get(idx)){
idx -= 1;
} else if (get(idx) < get(idx + 1)){
//题干条件严格保证数组中无重复的元素,故可以使用else if
idx += 1;
}
}
return idx;
}
};
时间复杂度:O(n),当数组单调的时候,最坏的情况O(n)仍有可能达到O(n)
空间复杂度:O(1)
法四:法三的二分查找优化
思路:
在法二基础上,我们若找到nums[i - 1] > nums[i],
那[i, n - 1]部分我们就不需要在遍历了,由于num[-1] = -∞,
所以肯定在[-1, i - 1]存在峰值
同理右边也是,故我们可以使用二分查找加快这一过程
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int n = nums.size();
auto get = [&](int i)->pair<int, int>{
if (i == -1 || i == n){
return {0, 0};
}
return {1, nums[i]};
};
int l = 0, r = n - 1, ans = -1;
while(l <= r){
int mid = l + (r - l) / 2;
if (get(mid - 1) < get(mid) && get(mid) > get(mid + 1)){
ans = mid;
break;
}
if (get(mid) < get(mid + 1)){
l = mid + 1;
} else if (get(mid - 1) > get(mid)){
r = mid - 1;
}
}
return ans;
}
};
时间复杂度:O(log n)
空间复杂度:O(1)
法五:贪心 + 二分查找
思路:
碰见上升则右侧必有峰
贪心:不去寻找可能最近的答案,而是每次尽可能用二分缩小范围
如果存在nums[mid] < nums[mid + 1],由于nums[n] = -∞ 那么一定存在下标(mid, n - 1]范围内,满足条件的峰值
我们使用二分查找加快这一进程:
如果nums[mid] < nums[mid + 1] 我们左边界l = mid + 1;
不然我们就r = mid,(由于在nums[mid] > nums[mid + 1],nums[mid]可能不是最大的,所以我们需要左移右边界,直至找到峰值)
代码一:
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int n = nums.size();
int l = 0, r = n - 1;
while(l < r){
int mid = l + (r - l) / 2;
if (nums[mid] < nums[mid + 1]){
l = mid + 1;
} else {
r = mid;
}
//缩写为
//nums[mid] < nums[mid + 1] ? l = mid + 1 : r = mid;
}
return l;
}
};
时间复杂度:O(log n)
空间复杂度:O(1)
代码二:
参考官方题解评论区下梦璃夜·天星
class Solution {
public:
int findPeakElement(vector<int>& nums) {
nums.push_back(INT_MIN);
int n = nums.size();
int l = 0, r = n - 2;
while(l < r){
int mid = l + (r - l) / 2;
//等号是由于上面我们尾插了一个可能跟数组元素相同的值INT_MIN
if (nums[mid] >= nums[mid + 1] && (mid == 0 || nums[mid] >= nums[mid - 1])){
return mid;
} else if (nums[mid] < nums[mid + 1]) {//上面有 =,无法取到等于,所以可以加减 一
l = mid + 1;
} else {
r = mid - 1;
}
}
return l;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
以上是关于《LeetCode之每日一题》:151.寻找峰值的主要内容,如果未能解决你的问题,请参考以下文章