1D Peak Finding - HolmesJJ/Data-Structures-and-Algorithms GitHub Wiki
题目
峰值元素是指其值大于左右相邻值的元素。给你一个输入数组arr,无重复元素,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可,你可以假设 nums[-1] = nums[n] = -∞(两端为-∞)。
详解
情况分析
情况 1. 所有的数字以降序排列:第一个元素即为峰值
情况 2. 所有的数字以升序排列:最后一个元素为峰值
情况 3. 峰值出现在中间某处:中间的某个元素为峰值
标准写法(无重复值)
方法一: 线性扫描(时间复杂度:O(n);空间复杂度:O(1))
public static int findPeakElement(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1])
return i;
}
return arr.length - 1;
}
方法二: 递归二分查找(时间复杂度:O(logn);空间复杂度:O(logn))
public static int findPeakElement(int[] arr) {
return search(arr, 0, arr.length - 1);
}
public static int search(int[] arr, int low, int high) {
// 为什么不是low > high?
// 因为下面不是mid - 1,肯定会出现low == high的情况
if (low == high) {
return low;
}
int mid = low + (high - low) / 2;
// 下坡
if (arr[mid] > arr[mid + 1]) {
return search(arr, low, mid);
}
// 上坡
else {
return search(arr, mid + 1, high);
}
}
方法三: 迭代二分查找(时间复杂度:O(logn);空间复杂度:O(1))
public static int findPeakElement(int[] arr) {
int low = 0, high = arr.length - 1;
// 写low != high也行
while (low < high) {
int mid = (low + high) / 2;
if (arr[mid] > arr[mid + 1])
high = mid;
else
low = mid + 1;
}
return low;
}
无重复值的二分法时间复杂度分析
T(1) = 1 T(n) = T(n/2) + O(1) T(n) = T(n/2^2) + O(1) + O(1) T(n) = T(n/2^3) + O(1) + O(1) + O(1) ... T(n) = T(n/2^k) + kO(1) 设 n = 2^k 得 k = logn T(n) = T(1) + lognO(1) = O(logn)
若数组arr中有重复元素
此时若arr[mid-1] = arr[mid] = arr[mid+1],将无法判断应该选左边还是选右边。若选错边,就永远都找不到峰值8。此时二分法就必须两边都要递归
标准写法(有重复值)
public static int findPeakElement(int[] arr) {
return search(arr, 0, arr.length - 1);
}
public static int search(int[] arr, int low, int high) {
// 为什么不是low > high?
// 因为下面不是mid - 1,肯定会出现low == high的情况
if (low == high) {
return low;
}
int mid = low + (high - low) / 2;
// 下坡
if (arr[mid] > arr[mid + 1]) {
return search(arr, low, mid);
}
// 上坡
else if (arr[mid] < arr[mid + 1]) {
return search(arr, mid + 1, high);
}
// 无法判断左右时,只能两边都递归,找到值较大的一边返回
else {
int leftMax = search(arr, low, mid);
int rightMax = search(arr, mid + 1, high);
if (arr[leftMax] < arr[rightMax]) {
return rightMax;
} else {
return leftMax;
}
}
}
有重复值的二分法时间复杂度分析
T(1) = 1 T(n) = 2T(n/2) + O(1) T(n) = 2^2 * T(n/2^2) + 2O(1) + O(1) T(n) = 2^3 * T(n/2^3) + 4O(1) + 2O(1) + O(1) ... T(n) = 2^k * T(n/2^k) + (2^k - 1)O(1) 设 n = 2^k 得 k = logn T(n) = nT(1) + (n - 1)O(1) = O(2n - 1) = O(n)