二分查找练习

文章目录

寻找峰值

峰值元素是指其值严格大于左右相邻值的元素
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。

你必须实现时间复杂度为 O(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]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-peak-element

方法一:
根据观察,如果存在峰值,那么这个峰值就会比他前后两个数字的值都要大,这时候只要拿当前这个元素的值和他前后两个元素的值进行比较,从而可以判断出这个元素是否为峰值。但是有两种情况需要考虑的是,就是连续升序和连续降序的情况,这时候,是没有办法进行比较前后两个元素的,此时只要保证了下标为0的元素比下标为1的元素的值要大,这时候就说明下标为0的元素是一个峰值,因为存在了降序的趋势,同样的,如果下标为nusm.length - 1的元素的值比他前一个元素的值要大,说明存在着升序的趋势,此时,nums.length - 1对应的值就是一个峰值,因为题目中已经明确说明了nums[-1] = nums[nums.length]= -∞,所以这两个端点只要满足了这两个条件,就必然是个峰值
否则,如果两个端点并没有满足条件,那么这时候说明下标为0的地方存在着升序的情况,而下标为nums.length - 1存在着降序,此时两者的中间必然存在峰值才会出现先升后降的趋势,因此,只要通过nums[i - 1] < nums[ i ] && nums[ i ] > nums[ i + 1]来找到峰值即可

class Solution {
    public int findPeakElement(int[] nums) {
         if(nums.length == 1)
             return 0;
         int i;
         for(i = 0; i < nums.length; ++i){
             if(i == 0 && nums[i] > nums[i + 1]){
    //如果是连续降序或者先降序后升序,那么始终第一个元素是一个峰值,所以可以直接返回下标0
                 return i;
             }else if(i == nums.length - 1 && nums[i] > nums[i - 1]){
    //如果连续升序,那么数组的最后一个元素才是峰值
                 return i;
             }else{
                 //数组并不是连续单调的,那么根据当前元素(不是端点)大于前后两个元素,从而得出当前元素是一个峰值,返回其下标
                 if(i > 0 && i < nums.length - 1 && nums[i - 1] < nums[i] && nums[i] > nums[i + 1])
                    return i;
             }
         }
         return -1;
    }
}

运行结果:
二分查找练习

方法二:二分查找
我们知道,二分查找只应用于一个数组有序的地方,为什么这里也可以应用呢?因为我们可以发现部分数组是有序的,而我们就是要找这部分有序数组的最大值。因此可以应用二分查找。

  • 在low <= high的情况下进行二分查找
  • 在low等于high的情况,直接返回low,表示当前元素就是一个峰值
  • 否则获取中间值mid = low + (high - low) / 2,当然也可以是mid = (high + low) / 2
  • 将nums[mid]和nums[mid + 1]进行比较,如果nums[mid]更大,说明[mid,mid + 1]之间存在着降序,此时mid对应的值可能是一个峰值,当然也可能不是,因为可能在[low,mid]可能是升序,也可能是降序的,所以mid对应的值不一定是峰值,此时需要更新high为mid,否则,如果nums[mid]更小,说明[mid,mid + 1]存在升序的趋势,此时mid必然不是峰值,而mid + 1也不一定是峰值,因为[mid + 1,high]可能是升序,也可能是降序,所以我们需要在[mid + 1,high]中寻找,所以更新low = mid + 1.

对应代码:

class Solution {

    /*
    利用二分查找,首先获得中间值nums[mid]
    这时候我们将其和mid + 1的值进行比较,如果mid的值更小,
    那么说明存在升序,所以这时候需要更新low = mid + 1
    否则,如果mid更大,说明存在降序这时候我们不可以
    直接考虑mid的值就是峰值,因为存在着连续降序的可能,所以
    不可以返回mid,而应该更新high=mid
    当low>high时候,直接返回low即可
    */
    public int findPeakElement(int[] nums) {
        int low = 0,high = nums.length - 1,mid;
        while(low <= high){
            if(low == high)//如果存在峰值,那么必然有low = high的情况
                return low;
            mid = low + (high - low) / 2;
            if(nums[mid] > nums[mid + 1]){
                //当前mid对应的值大于mid + 1对应的值,此时说明存在降序,此时不可以
                //返回mid,因为存在连续降序的情况,所以需要更新high为mid
                high = mid;
            }else{
                //当前的mid的值小于mid + 1的值,说明存在着升序,此时,需要更新low = mid + 1
                low = mid + 1;
            }
        }
        return -1;
    }


}

运行结果:
二分查找练习

上一篇:Leetcode_167. 两数之和 II - 输入有序数组


下一篇:【逐帧分析】《黑神话:悟空》gameplay相关的技术和调整细节整理