L52.【LeetCode题解】二分法习题集1

目录

1.模版回顾

2.LCR 072. x 的平方根

分析

代码

提交结果

3.搜索插入位置

分析

代码

提交结果

4.LCR 069. 山脉数组的峰顶索引

题目

分析

二分法代码

使用左边界模版

提交结果

使用右边界模版

提交结果

也可以分三部分

代码

提交结果


1.模版回顾

参见以下文章:

CC53.【C++ Cont】二分查找的普通模版

CC54.【C++ Cont】二分查找的左、右边界模版

2.LCR 072. x 的平方根

https://leetcode.cn/problems/jJ0w9p/description/

给定一个非负整数 x ,计算并返回 x 的平方根,即实现 int sqrt(int x) 函数。

正数的平方根有两个,只输出其中的正数平方根。

如果平方根不是整数,输出只保留整数的部分,小数部分将被舍去。

示例 1:

输入: x = 4
输出: 2

示例 2:

输入: x = 8
输出: 2
解释: 8 的平方根是 2.82842...,由于小数部分将被舍去,所以返回 2

提示:

  • 0 <= x <= 231 - 1

注意:本题与主站 69 题相同: 69. x 的平方根 - 力扣(LeetCode)

分析

L51.【LeetCode题解】LCR 072. x 的平方根 (6种方法)文章未讲的方法6:二分法

从暴力解法优化:

L51文章暴力解法:

由于题目说了"如果平方根不是整数,输出只保留整数的部分,小数部分将被舍去",那就可以一个一个枚举所有整数

class Solution {
public:
    int mySqrt(int x) 
    {
        for (long long i=0;i<=x;i++)
        {
            if (i*i<=x&&(i+1)*(i+1)>x)
                return i;
        }
        return -1;
    }
};

暴力解法的意思枚举1^22^23^2、......、n^2,直到某个数c的c^2不超过x的最大整数

如果从1开始枚举将会很慢,可以采用二分法

注意到不超过x的最大整数是小于等于x,符合右边界的要求

直接使用模版:

//能进入循环的前提是数组不为空!!!
while (left < right)
{
	int mid = left + (right - left + 1) / 2;
	if (......)
		left = mid;
	else
		right = mid - 1;
}

改一下if判断的处理即可

代码

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

注意if判断中是mid*mid,如果mid为int类型很有可能会溢出,因此将mid的数据类型的范围适当定大些

提交结果

3.搜索插入位置

https://leetcode.cn/problems/search-insert-position/description/

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

提示:

  • 1 <= nums.length <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • nums 为 无重复元素 的 升序 排列数组
  • -10^4 <= target <= 10^4

分析

题目已经提示时间复杂度为O(NlogN),读题"nums 为 无重复元素 的 升序 排列数组",可以利用数组的有序性来使用二分法

对于没有找到target的情况要在循环结束后单独判断,插入位置的特点:大于target的第一个数的下标

直接使用二段性划分:

 显然是寻找左边界,使用CC54.【C++ Cont】二分查找的左、右边界模版文章的左边界模版:

//能进入循环的前提是数组不为空!!!
while (left < right)
{
	int mid = left + (right - left) / 2;
	if (......)
		left = mid + 1;
	else
		right = mid;
}

注意这里不能直接使用模版,循环结束后还需要判断

因为如果nums[mid]==target,直接返回mid,因此

代码

while循环体的判断分2种情况:

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

或者while循环体的判断需要分3种情况:

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

        //left==right
        if (nums[left]>=target)
            return left;
        else    
            return left+1;
    }
};

left==right有两种情况

1.while循环修改left和right

2.没有进入while循环,数组中只有一个元素

因此在while循环结束后要写成if (nums[left]>=target),必须是>=,其中>=的=是考虑数组中只有一个元素的情况

提交结果

4.LCR 069. 山脉数组的峰顶索引

题目

符合下列属性的数组 arr 称为 山峰数组山脉数组)

  • arr.length >= 3
  • 存在 i0 < 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 = [1,3,5,4,2]
输出:2

示例 3:

输入:arr = [0,10,5,2]
输出:1

示例 4:

输入:arr = [3,4,5,1]
输出:2

示例 5:

输入:arr = [24,69,100,99,79,78,67,36,26,19]
输出:2

提示:

  • 3 <= arr.length <= 10^4
  • 0 <= arr[i] <= 10^6
  • 题目数据保证 arr 是一个山脉数组

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

分析

要求"设计一个 O(log(n)) 的解决方案",显然使用二分法,二分法的使用前提:符合二段性,显然山脉数组是符合的

(二段性的介绍在CC53.【C++ Cont】二分查找的普通模版文章)

例如以下山脉数组:

显然可以分两部分:

红框元素严格单调递增,arr[mid]>arr[mid-1]

蓝框元素严格单调递减,arr[mid]>arr[mid+1]

需要寻找红框的边界元素,即左边界,使用左边界模版:

//能进入循环的前提是数组不为空!!!
while (left < right)
{
	int mid = left + (right - left) / 2;
	if (......)
		left = mid + 1;
	else
		right = mid;
}

或者这样分:

红框元素严格单调递增,arr[mid]>arr[mid-1]

蓝框元素严格单调递减,arr[mid]>arr[mid+1]

需要寻找蓝框的边界元素,使用右边界模版: 

//能进入循环的前提是数组不为空!!!
while (left < right)
{
	int mid = left + (right - left + 1) / 2;
	if (......)
		left = mid;
	else
		right = mid - 1;
}

二分法代码

使用左边界模版

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

使用右边界模版

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

也可以分三部分

代码

class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) 
    {
        int left=0;
        int right=arr.size()-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if (arr[mid]>arr[mid+1]&&arr[mid]>arr[mid-1])
                return mid;
            if (arr[mid]<arr[mid+1]&&arr[mid]>arr[mid-1])
                left=mid;
            if (arr[mid]>arr[mid+1]&&arr[mid]<arr[mid-1])
                right=mid;
        }
        return 0;//能找到峰值,这里随意返回一个数
    }
};
提交结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangcoder

赠人玫瑰手有余香,感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值