初识算法 · 二分查找(3)

目录

前言:

x的平方根

题目解析

算法原理

算法编写

山脉数组的封顶索引

题目解析

算法原理

算法编写


前言:

​本文的主题是二分查找,通过两道题目讲解,一道是x的平方根,一道是山脉数组的封顶索引。
链接分别为:
69. x 的平方根 - 力扣(LeetCode)

852. 山脉数组的峰顶索引 - 力扣(LeetCode)
题目分为三个部分讲解,一是题目解析,二是算法原理,三是算法编写,那么,话不多说,直接进行主题咯。


x的平方根

题目解析

由题目的要求,我们需要求一个数的算法平方根,并且舍去小数部分,并且禁止我们使用pow函数等。说白了就是要我们自己模拟实现一个函数。

那么这道题的暴力解法就很简单了,将题目示例的所有平方列举出来,和题目给的x一个一个比较就可以了。当比较到x 小于某个数的平方,那么我们就可以得到x等于某个数减1.

但是这样做的话,时间复杂度就是O(N),并且没有利用数据是升序的特点。有人问了就,升序是哪里来的,我们从1开始列举平方,也就是一个升序的过程。

那么直接进入算法原理部分吧。

算法原理

我们利用二段性,就可以将数分为小于等于x和大于x的,那么我们的目标区域是小于等于x的右端点,所以我们使用模板是朴素模板呢?还是非朴素模板呢?肯定是非朴素求左端点的模板了。

所以left < right是必要的,但因为ans区域是在左边,所以left = mid,right = mid - 1,因为有了减法,所以求中间是 + 1,这些都是二分查找(2)的。

因为有了二分(2)的基础,所以我们直接进入算法编写部分。

算法编写

class Solution 
{
public:
    int mySqrt(int x) 
    {
        if(x <= 0) return 0;
        int left = 1, right = x;
        while(left < right)
        {
            long long mid = left + (right - left + 1) / 2;
            if(mid * mid <= x) left = mid;
            else right = mid - 1;
        }    
        return left;
    }
};

这里有两个小点需要注意的,是x可能为0,所以直接返回0即可,并且因为mid * mid,可能导致数据溢出的风险,所以需要long long。


山脉数组的封顶索引

题目解析

题目要求是让我们找到封顶的最高值,并且数组的大小是从高到低,所以暴力解法非常非常简单,如果判断了后面的值小于该值,那么该值就是我们要的值。

肯定是一个O(N)就能够解决的事儿,但是题目要求我们必须找到对应logN的解决方案,那么看都不用看,直接就是二分查找。

小细节就是,左右端点肯定不可能是结果,所以我们不妨让left从1开始,right从nums.size() - 2的值开始。

算法原理

对于算法原理来说,我们无非是要找到对应的二段性,所以峰值左边就是arr[i] > arr[i - 1],右边同理可得,二段性就是从峰值开始分开的。

由题目解析我们可以知道left和right的取值,并且因为我们见数组分成了这样,那么答案是在左边,所以我们使用的二分模板肯定是非朴素的求左边的二分模板了。

还是那句话,因为有了二分查找(2)的基础,所以我们可以直接套用模板了,就不会重新讲解一下原理了。

算法编写

class Solution 
{
public:
    int peakIndexInMountainArray(vector<int>& arr) 
    {
        int left = 1, right = arr.size() - 2;
        while(left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if(arr[mid] > arr[mid - 1]) left = mid;
            else right = mid - 1;
        }
        return left;
    }
};

就,非常简单~


感谢阅读!

猜你喜欢

转载自blog.csdn.net/2301_79697943/article/details/143169432