二分查找
文章目录
二分查找是在一个有序数组里,进行查找的一种算法
先来一题简单题看看二分查找是长什么样的
374.猜数字大小
List item
猜数字游戏的规则如下:
- 每轮游戏,我都会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。
- 如果你猜错了,我会告诉你,你猜测的数字比我选出的数字是大了还是小了。
你可以通过调用一个预先定义好的接口 int guess(int num)
来获取猜测结果,返回值一共有 3 种可能的情况(-1
,1
或 0
):
- -1:我选出的数字比你猜的数字小
pick < num
- 1:我选出的数字比你猜的数字大
pick > num
- 0:我选出的数字和你猜的数字一样。恭喜!你猜对了!
pick == num
返回我选出的数字。
public class Solution extends GuessGame {
public int guessNumber(int n) {
int left=1;
int right=n;
while(left<right)
{
int mid=left+(right-left)/2;
if(guess(mid)==-1)
{
right=mid;
}else if(guess(mid)==1)
{
left=mid+1;
}else{
return mid;
}
}
return left;
}
}
35.搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
class Solution {
public int searchInsert(int[] nums, int target) {
int left=0;
int right=nums.length;
int mid;
while(left<right) {
mid=(left+right)/2;
if(nums[mid]==target) {
return mid;
}else if(nums[mid]>target){
right=mid; //[left...mid]
}else{
left=mid+1; //[mid+1...right]
}
}
if(left==0)
{
return 0;
}
return left;
}
}
69. x 的平方根
给你一个非负整数 x
,计算并返回 x
的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
**注意:**不允许使用任何内置指数函数和算符,例如 pow(x, 0.5)
或者 x ** 0.5
。
class Solution {
public int mySqrt(int x) {
if(x==0) return 0;
long left=1;
long right=x;
while(left<right)
{
//为了防止溢出
long mid=left+(right-left)/2;
long s=mid*mid;
if(s>=x)
{
right=mid;
}else{
left=mid+1;
}
}
if(left*left>x)
{
return (int)(left-1);
}else{
return (int)left;
}
}
}
441. 排列硬币
你总共有 n
枚硬币,并计划将它们按阶梯状排列。对于一个由 k
行组成的阶梯,其第 i
行必须正好有 i
枚硬币。阶梯的最后一行 可能 是不完整的。
给你一个数字 n
,计算并返回可形成 完整阶梯行 的总行数。
![](https://i-blog.csdnimg.cn/blog_migrate/b26743263db45ffcb7984e9f651606a9.jpeg)
输入:n = 5
输出:2
解释:因为第三行不完整,所以返回 2 。
class Solution {
public int arrangeCoins(int n) {
int left = 1;
int right = n;
while (left <= right) {
int mid = left + (right - left) / 2;
long sum = (long) mid * (mid + 1) / 2;
if (sum == n) {
return mid;
} else if (sum < n) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
}
744. 寻找比目标字母大的最小字母
给你一个字符数组 letters
,该数组按非递减顺序排序,以及一个字符 target
。letters
里至少有两个不同的字符。
返回 letters
中大于 target
的最小的字符。如果不存在这样的字符,则返回 letters
的第一个字符。
class Solution {
public char nextGreatestLetter(char[] letters, char target) {
int left=0,right=letters.length-1;
if(letters[right]<=target || letters[left]>target)
{
return letters[0];
}
while(left<right)
{
int mid=left+(right-left)/2;
char c=letters[mid];
if(c>target)
{
right=mid;
}else if(c<=target)
{
left=mid+1;
}
}
return letters[left];
}
}
278. 第一个错误的版本
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n
个版本 [1, 2, ..., n]
,你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version)
接口来判断版本号 version
是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
boolean f=isBadVersion(1);
int left=1,right=n;
while(left<right)
{
int mid=left+(right-left)/2;
f=isBadVersion(mid);
if(f)
{
right=mid;
}else{
left=mid+1;
}
}
return left;
}
}
1346. 检查整数及其两倍数是否存在
给你一个整数数组 arr
,请你检查是否存在两个整数 N
和 M
,满足 N
是 M
的两倍(即,N = 2 * M
)。
更正式地,检查是否存在两个下标 i
和 j
满足:
i != j
0 <= i, j < arr.length
arr[i] == 2 * arr[j]
解题思路
检查是否存在两个数满足2倍关系,一眼二分
二分固定步骤:先进行排序
然后再把每个数取出来,放进while
二分查找里循环查找,在这里需要注意一个特殊值0
因为0*2==0,当时下标又不能为自己
所以进行了判断mid!=i
class Solution {
public boolean checkIfExist(int[] arr) {
int n = arr.length;
Arrays.sort(arr);
for (int i = 0; i < n; i++) {
int x = arr[i] * 2;
int left = 0;
int right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] == x && mid!=i) {
return true;
}else if (arr[mid] > x) {
right = mid;
}else{
left = mid + 1;
}
}
}
return false;
}
}
1351. 统计有序矩阵中的负数
给你一个 m * n
的矩阵 grid
,矩阵中的元素无论是按行还是按列,都以非递增顺序排列。 请你统计并返回 grid
中 负数 的数目。
解题思路
题目说明以非递增顺序排列
,就这个条件,一眼二分
二分查找循环每行,二分常规写法,找到left是正数和负数的交界位置,用长度减去left就是负数的个数
class Solution {
public int countNegatives(int[][] grid) {
int n=grid.length;
int m=grid[0].length;
int ans=0;
for(int i=0;i<n;i++)
{
int left=0;
int right=m;
while(left<right)
{
int mid=left+(right-left)/2;
if(grid[i][mid]>=0)
{
left=mid+1;
}else{
right=mid;
}
}
ans+=m-left;
}
return ans;
}
}
2529. 正整数和负整数的最大计数
给你一个按 非递减顺序 排列的数组 nums
,返回正整数数目和负整数数目中的最大值。
- 换句话讲,如果
nums
中正整数的数目是pos
,而负整数的数目是neg
,返回pos
和neg
二者中的最大值。
注意:0
既不是正整数也不是负整数。
解题思路
老规矩,看到非递减顺序,一眼看出二分,然后按二分的标准套路走即可
class Solution {
public int maximumCount(int[] nums) {
int left=0;
int right=nums.length;
int sum=0;
//判断边界条件
if(nums[0]>0 || nums[nums.length-1]<0) {//全为正数或全为负数
return nums.length;
}else if(nums[0]==0 && nums[nums.length-1]==0)//全部都是0
{
return 0;
}
while(left<right)
{
int mid=left+(right-left)/2;
if (nums[mid]<=0){
if(nums[mid]==0)
sum++;//计算0有多少个
left=mid+1;
}else{
right=mid;
}
}
if(nums[nums.length-1]==0)sum++;//最后这个0再判断一下,太边界了判断不到
right=nums.length-left;
left=left-sum; //因为left碰到0也要往右走
return Math.max(right,left);
}
}