LeetCode 162. 寻找峰值(简单二分)

题目描述:峰值元素指的是其值严格大于左右相邻的元素。给定一个整数数组,其可能包含多个峰值元素,返回任意一个峰值元素的下标。(可认为nums[-1] = nums[n] = -∞
要求解法达到 l o g ( n ) log(n) log(n)时间复杂度。

简单想法就是遍历一次,对每个元素判断是否是峰值即可。但是时间复杂度为 O ( n ) O(n) O(n)。要想达到 l o g ( n ) log(n) log(n)的复杂度,只能采用类似二分的思想,每次排除掉一半的元素才行。接下来就想怎么二分呢。

我们先随便找一个位置,这个位置的情况只可能是如下4种:
LeetCode 162. 寻找峰值(简单二分)_第1张图片

  1. 左右两侧的数都比它小
  2. 左右两侧的数都比它大
  3. 左右两侧的数一大一小(左小右大)
  4. 左右两侧的数一大一小(左大右小)
  • 对于1,则已经找到峰值,直接返回。
  • 对于2,峰值可能出现在左侧,也可能出现在右侧(左右两侧都一定存在峰值)
  • 对于3,峰值可能出现在左侧,也可能出现在右侧(左侧不一定存在峰值,但右侧一定存在,极端情况是左到右单调递增,则最后一个位置为峰值,因为可认为nums[n]为负无穷)
  • 对于4,峰值可能出现在左侧,也可能出现在右侧(右侧不一定存在峰值,但左侧一定存在,极端情况是左到右单调递减,则第一个位置为峰值,因为可认为nums[-1]为负无穷)

由于只需要找到一个峰值即可,那么只需要进行二分,每次往一定存在峰值的一端走即可(往更高处走)

我自己的代码(加了太多边界特判了,后来发现其实并不需要)

class Solution {
    public int findPeakElement(int[] nums) {
        return find(nums, 0, nums.length - 1);
    }

    private int find(int[] nums, int l, int r) {
        if (l > r) return -1;
        if (l == r) {
            if (l == 0 && l == nums.length - 1) return l;
            if (l == 0) return nums[0] > nums[1] ? 0 : -1;
            if (l == nums.length - 1) return nums[l] > nums[l - 1] ? l : -1;
            return nums[l] > nums[l - 1] && nums[l] > nums[l + 1] ? l : -1;
        }
        int mid = l + r >> 1;
        if (mid == 0) {
            if (nums[0] < nums[1]) return find(nums, mid + 1, r);
            else return 0;
        }
        if (mid == nums.length - 1) {
            if (nums[mid] < nums[mid - 1]) return find(nums, l, mid - 1);
            else return mid;
        }
        // 4种情况
        if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) return mid;
        if (nums[mid] < nums[mid - 1] && nums[mid] < nums[mid + 1]) return find(nums, mid + 1, r);
        if (nums[mid] > nums[mid - 1] && nums[mid] < nums[mid + 1]) return find(nums, mid + 1, r);
        if (nums[mid] < nums[mid - 1] && nums[mid] > nums[mid + 1]) return find(nums, l, mid - 1);
        return -1;
    }
}

看完题解后,发现只需要简单二分就行了,10行代码左右

class Solution {
    public int findPeakElement(int[] nums) {
        int l = 0, r = nums.length - 1;
        // 若只有一个元素, 则直接跳出循环
        while (l < r) {
            int mid = l + r >> 1; // 若只有2个元素, 保证中点取到左侧0
            if (nums[mid] > nums[mid + 1]) r = mid;
            else l = mid + 1;
        }
        return l;
    }
}

总结:二分,每次都往更高的一端走(人往高处走),一定能找到一个峰值。

你可能感兴趣的:(算法,leetcode,算法,二分)