LeetCode 山脉数组的峰顶索引/寻找峰值

905 阅读2分钟

这是我参与更文挑战的第 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 <= 104
  • 0 <= arr[i] <= 106
  • 题目数据保证 arr 是一个山脉数组

**进阶:**很容易想到时间复杂度 O(n) 的解决方案,你可以设计一个 O(log(n)) 的解决方案吗?

思路分析

O(n)复杂度的解决方案确实很容易相处,对于这种山脉类的题目,我们也可以用二分法来解决,题目说到只存在一顶峰,那么题目就比较简单了,可以简单划分为上坡和下坡,最顶点处高于它的左边点和右边点。我们将情况分为上坡和下坡两种,然后进行判断。

这道题可以不使用常用的二分法套路,因为情况很简单,可以直接分为两种情况。

代码展示

解法一:时间复杂度是O(logn){O(logn)},空间复杂度是O(1){O(1)}

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;

对于非确定性的查找,使用前后探测法,来确定搜索区间。先处理命中情况,再处理左右半部分查找的情况。

代码展示

解法一:时间复杂度是O(logn){O(logn)},空间复杂度是O(1){O(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;

    }

总结

峰值类的题目也存在一定的套路,只要我们熟悉二分法的解决场景,这类题目就可以迎刃而解。