这是我参与更文挑战的第 14 天,活动详情查看: 更文挑战
山脉数组的峰顶索引(852)
题目描述
符合下列属性的数组 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 。
示例 1:
输入: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
提示
3 <= arr.length <= 1040 <= arr[i] <= 106- 题目数据保证
arr是一个山脉数组
**进阶:**很容易想到时间复杂度 O(n) 的解决方案,你可以设计一个 O(log(n)) 的解决方案吗?
思路分析
O(n)复杂度的解决方案确实很容易相处,对于这种山脉类的题目,我们也可以用二分法来解决,题目说到只存在一顶峰,那么题目就比较简单了,可以简单划分为上坡和下坡,最顶点处高于它的左边点和右边点。我们将情况分为上坡和下坡两种,然后进行判断。
这道题可以不使用常用的二分法套路,因为情况很简单,可以直接分为两种情况。
代码展示
解法一:时间复杂度是,空间复杂度是。
public int peakIndexInMountainArray(int[] arr) { //[0,10,5,4,3,2,1]
int low = 0;
int high = arr.length - 1; // >=3
while (low <= high){
int mid = low + (high - low)/2; //arr[4] = 2
if (arr[mid] > arr[mid - 1]){ //上坡
if (arr[mid] > arr[mid + 1]){
return mid;
}
// mid < mid + 1 //上坡
low = mid + 1;
} else { //下坡
if (arr[mid - 1] > arr[mid - 2]){
return mid - 1;
}
high = mid - 1;
}
}
return -1;
}
寻找峰值(162)
题目描述
峰值元素是指其值大于左右相邻值的元素。
给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
进阶:
- 你可以在
O(n 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
- -231 <= nums[i] <= 231 - 1
- 对于所有有效的 i 都有 nums[i] != nums[i + 1]
思路分析
题目中提到”可以假设 nums[-1] = nums[n] = -∞ “,也就是说最左侧和最右侧都是无穷小,这个对于峰值的判断也很重要。这道题和上面那道题目是同一类型的题目,但是由于存在多个峰值,我们只需要取出一个峰值,所以会有更多的边界条件需要处理。
二分查找有一定的套路,首先定义 low 和 high,while 循环的判断条件一直是 low <= high,对于 low == high,必要的时候特殊处理,在 while 内部补充,返回值永远是 mid;low、high 的更新永远是 low = mid + 1 和 high = mid - 1;
对于非确定性的查找,使用前后探测法,来确定搜索区间。先处理命中情况,再处理左右半部分查找的情况。
代码展示
解法一:时间复杂度是,空间复杂度是。
public int findPeakElement(int[] nums) {
int low = 0;
int high = nums.length - 1;
if (high == 0){
return 0;
}
if (high == 1){
if (nums[0] < nums[1]){
return 1;
}
return 0;
}
while (low <= high){ //6,5,4,3,2,3,2 //5,4,3,4,5
int mid = low + (high - low)/2;
if (mid == 0){
return 0;
}
if (mid == nums.length - 1){
return mid;
}
if (nums[mid] > nums[mid - 1]){ //上坡
if (mid == nums.length - 1){
return mid;
}
if (nums[mid] > nums[mid + 1]){
return mid;
}
// mid < mid + 1 //上坡
low = mid + 1;
} else { //下坡 nums[mid] < nums[mid - 1]
if (mid == 1){
return 0;
}
if (mid >= 2 && nums[mid - 1] > nums[mid - 2]){
return mid - 1;
}
high = mid - 1;
}
}
return -1;
}
总结
峰值类的题目也存在一定的套路,只要我们熟悉二分法的解决场景,这类题目就可以迎刃而解。