二分刷题讲解
一.模板
y总的两个模板:
(下面两个模板只适用于在区间内存在要寻找的值时才适用,当然不存在也可以用只是要对模板修改)
模板1
//更新方式为l=mid+1,r=mid
int binarySearch(int l,int r)
{
int mid;
while(l<r)
{
mid = (l+r)/2;//写成l+(r-l)/2可以防止越界,这在数据量比较大的时候就更加适用
if(check(mid))
r=mid;
else
l=mid+1;
}
return l;
}
模板2
//更新方式为l=mid,r=mid-1
int binarySearch(int l,int r)
{
int mid;
while(l<r)
{
mid = (l+r+1)/2;//防止死循环,可以拿1,2,3数组,寻找3试一下不+1,这时会死循环
if(check(mid))
l=mid;
else
r=mid+1;
}
return l;
}
二.题目y总的讲解笔记
1.求平方根Leedcode–69
注意要用long最后强转一下,因为用int尤其在mid*mid那个地方绝对会越界
下面套用两个模板,其中第一个成功通过,第二个失败,具体原因分析如注释:
模板2
class Solution {
//该种写法最后返回的要么是mid-1,要么是mid,而条件是mid*mid>x,假如mid*mid>x,返回mid-1,为正确值,如果mid*mid<=x,返回mid也为正确值
public int mySqrt(int x) {
long l=0,r=x;
long mid;
while(l<r)
{
mid=l+(r-l+1)/2;
if(mid*mid>x)
r=mid-1;
else
l=mid;
}
return (int)l;
}
}
题目描述:
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842…,
由于返回类型是整数,小数部分将被舍去。
模板1
class Solution {
//该种写法最后返回的要么是mid+1,要么是mid,而条件是mid*mid<=x,假如mid*mid<=x,返回mid+1,为比正确值大一,如果mid*mid>x,返回mid也为比正确值大一
public int mySqrt(int x) {
long l=0,r=x;
long mid;
while(l<r)
{
mid=l+(r-l)/2;
if(mid*mid<=x)
l=mid+1;
else
r=mid;
}
return (int)l;
}
}
下面对模板1进行改进(提交通过)
class Solution{
public int mySqrt(int x) {
long l=0,r=x;
//0与1要单独讨论
if(r==0)
return 0;
if(r==1)
return 1;
long mid;
while(l<r)
{
mid=l+(r-l)/2;
if(mid*mid<=x)
l=mid+1;
else
r=mid;
}
return (int)l-1;
}
}
总结:千万不要为了用哪一个模板而改模板,而应该选择适用的的那一个
2.Leedcode—39
题目描述:
给定一个按照升序排列的整数数组 nums,和一个目标值 target。
找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] a=new int[2];
a[0]=a[1]=-1;
if(nums.length==0)
return a;
int l=0,r=nums.length-1;
int mid;
while(l<r)
{
mid = (l+r+1)/2;
//注意等于号不可以掉,因为如果去掉当nums[mid]==target时,那么此时r=mid-1就直接将最后一个target淘汰掉了,下同
if(nums[mid]<=target)//小于等于那么此时最后一个target必然在mid右边或就是mid
l=mid;
else
r=mid-1;
}
//防止target不存在
if(nums[l]!=target)
{
return a;
}
a[1]=l;
l=0;
r=nums.length-1;
while(l<r)
{
mid = (l+r)/2;
if(nums[mid]>=target)//大于等于那么此时第一个target必然在mid左边或等于mid
r=mid;
else
l=mid+1;
}
a[0]=l;
return a;
}
}
3.Leedcode–74
题目描述:
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
输出: true
示例 2:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
输出: false
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int row=matrix.length;
if(row==0)
return false;
int col=matrix[0].length;
if(col==0)
return false;
int i,j;
if(row==0)
{
return false;
}
int l=0,r =row*col-1;
int mid ;
while(l<r)
{
mid = (l+r)/2;
if(((l+r)/2+1)%col==0){
i=((l+r)/2+1)/col-1;
j=col-1;
}
else
{
i=((l+r)/2+1)/col;
j=((l+r)/2+1)%col-1;
}
if(matrix[i][j]>=target)
r=mid;
else
l=mid+1;
}
if(((l+r)/2+1)%col==0){
i=((l+r)/2+1)/col-1;
j=col-1;
}
else
{
i=((l+r)/2+1)/col;
j=((l+r)/2+1)%col-1;
}
if(matrix[i][j]!=target)
return false;
else
return true;
}
}
升级版:搜索二维矩阵 II
4.Leedcode-153
题目描述:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
你可以假设数组中不存在重复元素。
示例 1:
输入: [3,4,5,1,2]
输出: 1
示例 2:
输入: [4,5,6,7,0,1,2]
输出: 0
class Solution {
//使用二分的思想来解决问题
public int findMin(int[] nums) {
int mid;
int left = 0;
int right = nums.length-1;
if(nums[0]<nums[nums.length-1])
return nums[0];
else{
//左半部分在nums[0]上面,右半部分在nums[0]下面
while(left<right){
mid = left+right>>1;
if(nums[mid]<nums[0])
right = mid;
else
left = mid+1;
}
}
return nums[left];
}
}
5.Leedcode-162
题目描述:
峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
你可以假设 nums[-1] = nums[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。
说明:
你的解法应该是 O(logN) 时间复杂度的。
class Solution {
public int findPeakElement(int[] nums) {
if(nums.length==1)
return 0;
if(nums[nums.length-1]>nums[nums.length-2])
return nums.length-1;
int left = 0;
int right = nums.length-1;
int mid;
while(left<right){
mid = left+right>>1;
if(mid<=nums.length-2&&nums[mid]>nums[mid+1])
right=mid;
else
left = mid+1;
}
return left;
}
}
这个问题的升级版为二维矩阵峰值的查找,如果你觉得本问题难度不大,可以参考这篇文章提升自己:二维峰值查找