【LeetCode 二分查找专项】寻找峰值(162)

1. 题目

峰值元素是指其值大于左右相邻值的元素。

给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 n u m s [ − 1 ] = n u m s [ n ] = − ∞ nums[-1] = nums[n] = -\infty nums[1]=nums[n]=

1.1 示例

  • 示例 1 1 1
  • 输入: nums = [1, 2, 3, 1]
  • 输出: 2 2 2
  • 解释: 3 3 3 是峰值元素,你的函数应该返回其索引 2 2 2
  • 示例 2 2 2
  • 输入: nums = [1, 2, 1, 3, 5, 6, 4]
  • 输出: 1 1 1 5 5 5
  • 解释: 你的函数可以返回索引 1 1 1,其峰值元素为 2 2 2;或者返回索引 5 5 5,其峰值元素为 6 6 6

1.2 说明

1.3 提示

  • 1 < = n u m s . l e n g t h < = 1000 1 <= nums.length <= 1000 1<=nums.length<=1000
  • − 2 31 ≤ n u m s [ i ] ≤ 2 31 − 1 -2^{31} \le nums[i] \le 2^{31} - 1 231nums[i]2311
  • 对于所有有效的 i 都有 nums[i] != nums[i + 1]

1.4 进阶

你可以实现时间复杂度为 O ( l o g N ) O(logN) O(logN) 的解决方案吗?

2. 解法一

2.1 分析

这道题,最最最重要的是条件,条件,条件:

  • 两边都是负无穷;
  • 只需返回一个波峰的索引即可。

这道题主要的问题是,数组当中可能有很多波峰,也可能只有一个,如果尝试画图,就跟股票信息一样,没有规律,那如何在使用二分法时根据中点值判断我们的二分方向该往何处去?

实际上,你这样想,中点所在地方,可能是某座山的不同位置:山峰、山的下坡处、山的上坡处。如果是山峰,最后会二分终止。关键是在下坡和上坡处如何确定下一次查找的方向,因为我们并不知道山峰在当前 mid 的左边还是右边。对此,记住两个字就可以了,爬山(没错,就是带你去爬山):

  • 如果你往下坡方向走,也许可能遇到新的山峰,但是也许是一个一直下降的坡,最后到边界;
  • 如果你往上坡方向走,就算最后一直上到边界,由于最边界是负无穷,所以一定能找到山峰。

总的一句话,在找到山峰之前往上坡的方向上继续二分,一定能找到,往下坡的方向可能找到,也可能找不到。

2.2 解答

from typing import List


class Solution:
    def find_peak_element(self, nums: List[int]) -> int:
        left, right = 0, len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if len(nums) == 1 or (left == 0 and nums[0] >= nums[1]):
                return 0
            if right == len(nums) - 1 and nums[-1] > nums[-2]:
                return len(nums) - 1
            if nums[mid - 1] < nums[mid] > nums[mid + 1]:
                return mid
            elif nums[mid - 1] > nums[mid] > nums[mid + 1]:
                right = mid
            else:  # When in trough, either side is okay.
                left = mid


def main():
    nums = [1, 2, 1, 3, 5, 6, 4]
    sln = Solution()
    print(sln.find_peak_element(nums))  # 5


if __name__ == '__main__':
    main()

实际上,第 17 17 17 行的 else 包含了两种情况,即:

  • nums[mid - 1] < nums[mid] < nums[mid + 1] ,此时往右相当于爬山
  • nums[mid - 1] > nums[mid] < nums[mid + 1] ,此时相当于 nums[mid] 在谷底,向任意一边都算是爬山
  • 时间复杂度: l o g 2 ( n ) log_2(n) log2(n)。每一步都将搜索空间减半。因此,总共只需要 l o g 2 ( n ) log_2(n) log2(n) 步。其中 nnums 数组的长度;
  • 空间复杂度: O ( 1 ) O(1) O(1)。只使用了常数空间。

猜你喜欢

转载自blog.csdn.net/weixin_37780776/article/details/120047479