力扣【二分查找】:852. 山脉数组的峰顶索引
- 题目描述:
给定一个长度为 n 的整数 山脉 数组 arr ,其中的值递增到一个 峰值元素 然后递减。
返回峰值元素的下标。
你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。
- 算法思路:
按照题目,山峰数组会递增到峰值元素然后递减,因此在山峰的某一侧不会出现相同的数值,但山峰两侧可能出现相同数值,
因为时间复杂度要求为O(log(n)),并且数组的两侧分别单调递增和单调递减,显然应该考虑用二分查找,
又因为数组两侧的单调性不同,假设下标为peak的峰值元素一定具有nums[peak - 1] < nums[peak] && nums[peak] > nums[peak + 1]这样的性质,
而对于除了峰值元素的其他元素,假设下标idx,则一定有nums[idx - 1] < nums[idx] 或者nums[idx] > nums[idx + 1],
那么容易得到,如果mid满足peak要求则mid一定是峰值元素,
而如果mid满足其他两种情况的其中一种,就说明mid位于山坡,相应调整left和right区间即可,
并且因为山峰数组长度大于等于3,上述的判断也不会出现越界
另一种思路:我这里的思路更偏向于把peak当作特殊情况单独处理,但其实可以简化成两个区间,比如nums[idx] < nums[idx + 1]为左侧区间,需要left = mid + 1,
而nums[idx] >= nums[idx + 1]则当作右侧区间,此时peak有可能位于这个区间,于是需要right = mid.
这种思路对之后的第1095题山脉数组查找目标值的优化有很大帮助
- 代码:
时间复杂度:O(log (n))
int peakIndexInMountainArray(vector<int>& arr) {
int left = 0, right = arr.size() - 1;
int result = -1;
while (left + 1 < right) {
int mid = left + (right - left) / 2;
if (arr[mid - 1] < arr[mid] && arr[mid] > arr[mid + 1]) {
result = mid;
break;
} else if (arr[mid - 1] < arr[mid]) {
left = mid;
} else {
right = mid;
}
}
return result;
}
另一种思路:
int peakIndexInMountainArray(vector<int>& arr) {
int left = 0, right = arr.size() - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] < arr[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
浙公网安备 33010602011771号