852. 山脉数组的峰顶索引
难度简单222 收藏 分享 切换为英文 接收动态 反馈
符合下列属性的数组 arr
称为 山脉数组 :
arr.length >= 3
- 存在
i
(0 < i < arr.length - 1
)使得:arr[0] < arr[1] < ... arr[i-1] < arr[i]
arr[i] > arr[i+1] > ... > arr[arr.length - 1]
给你由整数组成的山脉数组 arr
,返回任何满足 arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i + 1] > ... > arr[arr.length - 1]
的下标 i
。
示例
输入:arr = [0,1,0]
输出:1
示例 2:
输入:arr = [0,2,1,0]
输出:1
示例 3:
输入:arr = [0,10,5,2]
输出:1
示例 4:
输入:arr = [3,4,5,1]
输出:2
示例 5:
输入:arr = [24,69,100,99,79,78,67,36,26,19]
输出:2
提示:
3 <= arr.length <= 104
0 <= arr[i] <= 106
题目数据保证 arr 是一个山脉数组
进阶:很容易想到时间复杂度 O(n) 的解决方案,你可以设计一个 O(log(n)) 的解决方案吗?
我的解法:
尝试了“三分法”,但是怎么也没写出来。。。
最终暴力解决之:
class Solution:
def peakIndexInMountainArray(self, arr: List[int]) -> int:
length = len(arr)
l, m, r = 0, 1, 2
while (arr[l] < arr[m]) and (arr[m] > arr[r]) == False:
l, m, r = l + 1, m + 1, r + 1
return m
高赞题解中的“三分法”
[852. 山脉数组的峰顶索]顺序&二分&三分求解 - 山脉数组的峰顶索引 - 力扣(LeetCode) (leetcode-cn.com)
对于查找极值点,也可以采用三分的方法,即用两个点将区间三等分,通过比较两个三等分点可以排除掉左区间或右区间
若左等分点大于右等分点,有两种情况:极大值点在左区间或中区间,所以可以排除右区间
若左等分点小于右等分点,有两种情况:极大值点在中区间或右区间,所以可以排除左区间
这个和前文书中的“三分法”思路是一致的!我没有写出来,是因为if-else分支里,没有减一。且rmid用的是rmid = l + (r - l) // 3 * 2
,此题解中用的是rmid = r (r - l) // 3 * 2
,二分或是三份,都要从两边往中间逼近,我写的语句没有从两边往中间逼近,故找不到正确答案
class Solution:
def peakIndexInMountainArray(self, arr: List[int]) -> int:
# 三分查找最大值
left, right = 0, len(arr) - 1
while left < right:
m = (right - left) // 3
m1 = left + m
m2 = right - m
if arr[m1] > arr[m2]:
right = m2 - 1
else:
left = m1 + 1
return left
另一个“三分法”的题解:
【宫水三叶】二分 & 三分极值问题 - 山脉数组的峰顶索引 - 力扣(LeetCode) (leetcode-cn.com)
顾名思义,「三分」就是使用两个端点将区间分成三份,然后通过每次否决三分之一的区间来逼近目标值。
具体的,由于峰顶元素为全局最大值,因此我们可以每次将当前区间分为 [l, m1][l,m1]、[m1, m2][m1,m2] 和 [m2, r][m2,r] 三段,如果满足 arr[m1] > arr[m2]arr[m1]>arr[m2],说明峰顶元素不可能存在与 [m2, r][m2,r] 中,让 r = m2 - 1r=m2−1 即可。另外一个区间分析同理。
class Solution {
public int peakIndexInMountainArray(int[] arr) {
int n = arr.length;
int l = 0, r = n - 1;
while (l < r) {
int m1 = l + (r - l) / 3;
int m2 = r - (r - l) / 3;
if (arr[m1] > arr[m2]) {
r = m2 - 1;
} else {
l = m1 + 1;
}
}
return r;
}
}
官方题解:
好吧,官方的暴力法也更有智慧
class Solution:
def peakIndexInMountainArray(self, arr: List[int]) -> int:
n = len(arr)
ans = -1
for i in range(1, n - 1):
if arr[i] > arr[i + 1]:
ans = i
break
return ans
二分法:
class Solution:
def peakIndexInMountainArray(self, arr: List[int]) -> int:
n = len(arr)
left, right, ans = 1, n - 2, 0
while left <= right:
mid = (left + right) // 2
if arr[mid] > arr[mid + 1]:
ans = mid
right = mid - 1
else:
left = mid + 1
return ans