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;} };
暴力解法的意思枚举、
、
、......、
,直到某个数c的
是不超过x的最大整数
如果从1开始枚举将会很慢,可以采用二分法
注意到不超过x的最大整数是小于等于x,符合右边界的要求
直接使用模版:
//能进入循环的前提是数组不为空!!!
while (left < right)
{int mid = left + (right - left + 1) / 2;if (......)left = mid;elseright = 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;elseright = 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
分析
题目已经提示时间复杂度为,读题"
nums
为 无重复元素 的 升序 排列数组",可以利用数组的有序性来使用二分法
对于没有找到target的情况要在循环结束后单独判断,插入位置的特点:大于target的第一个数的下标
直接使用二段性划分:
显然是寻找左边界,使用CC54.【C++ Cont】二分查找的左、右边界模版文章的左边界模版:
//能进入循环的前提是数组不为空!!!
while (left < right)
{int mid = left + (right - left) / 2;if (......)left = mid + 1;elseright = 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==rightif (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==rightif (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
- 存在
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 = [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;elseright = mid;
}
或者这样分:
红框元素严格单调递增,arr[mid]>arr[mid-1]
蓝框元素严格单调递减,arr[mid]>arr[mid+1]
需要寻找蓝框的边界元素,使用右边界模版:
//能进入循环的前提是数组不为空!!!
while (left < right)
{int mid = left + (right - left + 1) / 2;if (......)left = mid;elseright = 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;elseright=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;//能找到峰值,这里随意返回一个数}
};