目录
二分查找的前提:
1)目标函数单调性(单调递增或者递减)
2)存在上下界(bounded)
3) 能够通过索引访问
代码模版
left , right = 0, len(arr)-1
while left <= right:
mid = left + (right- left)/2
if arr[mid] == target:
return result
elif arr[mid]< target:
left = mid +1
else:
right = mid-1
题目一:寻找重复数
(题目链接:https://leetcode-cn.com/problems/find-the-duplicate-number/)
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
自己的思路:采用二分法,即先对数组排序,然后在排序后的数组中使用二分法
class Solution {
public int findDuplicate(int[] nums) {
if(nums.length == 0){
return 0;
}
Arrays.sort(nums);
int res = -1;
int l =0;
int r = nums.length-1;
while(l<r){
int middle = l+(r-l)/2;
//System.out.println("middle: "+middle);
if(middle==0){
if(nums[middle] == nums[middle+1]){
return nums[middle];
}
}else if(middle==nums.length-1){
if(nums[middle] == nums[middle-1]){
return nums[middle];
}
}else{
if(nums[middle] == nums[middle-1] || nums[middle] == nums[middle+1]){
return nums[middle];
}
}
if(nums[middle] > middle){
l = middle+1;
}
else{
r = middle-1;
//res = nums[middle];
}
}
return res;
}
}
官方题解
不用对数组排序,每次统计数组中小于等于middle数的个数,利用while(l<=r),最后return l;
class Solution {
public int findDuplicate(int[] nums) {
int l =0;
int r = nums.length;
while(l<=r){
int middle = (l+(r-l)/2);
int count =0;
for (int a : nums){
if(a<= middle){
count++;
}
}
if(count<=middle){
l = middle+1;
}else{
r = middle-1;
}
}
return l;
}
}
题目二:0到n-1中缺失的数字
(题目链接:https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof/)
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。
在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
思路:二分法:注意索引和数字的不同。重点比较的是middle和nums[middle]的大小。
class Solution {
public int missingNumber(int[] nums) {
int l =0;
int r= nums.length-1;
while(l<=r){
int middle = l + (r-l)/2;
if(middle<nums[middle]){
r = middle-1;
}else{
l = middle+1;
}
}
return l;
}
}
题目三:有序数组中的单一元素
(题目链接:https://leetcode-cn.com/problems/single-element-in-a-sorted-array/)
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例 1:
输入: [1,1,2,3,3,4,4,8,8]
输出: 2
思路:
采用二分法,但是注意到,每次让middle处于索引位置的奇数位置,这样middle两侧就都是偶数项。
最开始middle是奇数位置,但是后面要想让middle继续处于奇数位置,就应该
if(middle%2 == 0) { middle= middle-1}
另外注意middle等于后一项,则需要跳个两个元素。
class Solution {
public int singleNonDuplicate(int[] nums) {
int l = 0;
int r = nums.length-1;
while(l<r){
int middle = l+ (r-l)/2;
if(middle %2 ==1){
middle = middle-1;
}
if(nums[middle]==nums[middle+1]){
l = middle+2;
}else{
r = middle-1;
}
}
return nums[l];
}
}
[注意]:为啥while中是l<r而不是l<=r,因为代码中有nums[middle]== nums[middle+1]防止索引到达数组最后一个,然后middle+1之后就会溢出。
题目四:x的平方根
(题目链接:https://leetcode-cn.com/problems/sqrtx/)
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2
思路:采用二分搜索:从0开始一直搜索到x
class Solution {
public int mySqrt(int x) {
int l =0;
int r = x;
int res = -1;
while(l<=r){
int middle = l+(r-l)/2;
if((long)middle *middle <= x){
l = middle+1;
res = middle;
}else{
r = middle-1;
}
}
return res;
}
}