【数据结构与算法】二分查找法合集

搜索插入位置

LeetCode:搜索插入位置

题目描述:

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

你可以假设数组中无重复元素。

示例:

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

思想:

正常的二分查找

代码:

递归方式

class Solution {
    public int searchInsert(int[] nums, int target) {
        return binarySearch(0,nums.length-1,nums,target);
    }
    private int binarySearch(int low, int high, int[] nums, int target){
        if(high<low){
            return low;
        }
        int mid = (high + low)/2;
        if(nums[mid] > target){
            return binarySearch(low,mid - 1,nums,target);
        }else if(nums[mid] < target){
            return binarySearch(mid + 1, high,nums,target);
        }else{
            return mid;
        }
    }
}

循环方式

public int binarySearch(int[] nums, int key) {
    int l = 0, h = nums.length - 1;
    while (l <= h) {
        int m = l + (h - l) / 2;
        if (nums[m] == key) {
            return m;
        } else if (nums[m] > key) {
            h = m - 1;
        } else {
            l = m + 1;
        }
    }
    return -1;
}

x的平方根

LeetCode:X的平方根

题目描述:

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 
     由于返回类型是整数,小数部分将被舍去。

思想:

二分查找的变种。注意不要用m * m == x来判断,m * m会越界,要用m==x/m来做判断条件。

代码:

class Solution {
    public int mySqrt(int x) {
        if(x<=1)
            return x;
        int low=0;
        int high=x;
        int m=x;
        while(high>=low){
            m=low+(high-low)/2;
            if(m==x/m){
                return m;
            }else if(m>x/m){
                high=m-1;
            }else if(m<x/m){
                low=m+1;
            }
        }
        return high;
    }
}

寻找比目标字母大的最小字母

LeetCode:寻找比目标字母大的最小字母

题目描述:

给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。

在比较时,字母是依序循环出现的。举个例子:

如果目标字母 target = 'z' 并且字符列表为 letters = ['a', 'b'],则答案返回 'a'

示例:

输入:
letters = ["c", "f", "j"]
target = "a"
输出: "c"

输入:
letters = ["c", "f", "j"]
target = "c"
输出: "f"

输入:
letters = ["c", "f", "j"]
target = "d"
输出: "f"

思想:

注意有多个重复元素的情况,要把<和==的情况合在一起进行处理,得到右边界。

代码:

class Solution {
    public char nextGreatestLetter(char[] letters, char target) {
        int l=0,h=letters.length-1,mid;
        while(h>=l){
            mid = (h+l)/2;
            if(letters[mid]>target)
                h=mid-1;
            else
                l=mid+1;
        }
        return letters[l%letters.length];
    }
}

有序数组的单一元素

LeetCode:有序数组的单一元素

题目描述:

给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

示例:

输入: [3,3,7,7,10,11,11]
输出: 10

思想:

这题的关键在于,mid每次都要取偶数,再判断nums[mid]==nums[mid+1],即可得知结果在左半边还是右半边。

代码:

class Solution {
    public int singleNonDuplicate(int[] nums) {
        int low=0,high=nums.length-1,mid;
        while(high>low){
            mid = low+(high-low)/2;
            if(mid%2==1) mid--;
            if(nums[mid]==nums[mid+1])
                low = mid+2;
            else
                high = mid;
        }
        return nums[low];
    }
}

第一个错误版本

LeetCode:第一个错误版本

题目描述:

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例:

给定 n = 5,并且 version = 4 是第一个错误的版本。

调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true

所以,4 是第一个错误的版本。 

思想:

二分查找的变种,要找true的最左边。判断条件比较特殊,不能生搬硬套3.寻找比目标字母大的最小字母一题中的规律。

代码:

/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int low=1,high=n,mid;
        while(high>low){
            mid = low+(high-low)/2;
            if(isBadVersion(mid)){
                high = mid;
            }else{
                low = mid+1;
            }
        }
        return low;
    }
}

寻找旋转排序数组中的最小值

LeetCode:寻找旋转排序数组中的最小值

题目描述:

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

示例:

输入: [4,5,6,7,0,1,2]
输出: 0

思想:

二分查找变种,主要是判断条件 nums[mid]>=nums[0] 需要细细琢磨。

代码:

class Solution {
    public int findMin(int[] nums) {
        int low=0,high=nums.length-1,mid;
        while(high>=low){
            mid = low+(high-low)/2;
            if(nums[mid]>=nums[0]){
                low = mid+1;
            }else{
                high = mid -1;
            }
        }
        return nums[low%nums.length]; 
    }
}

二分查找变种总结

标准形式:

public int binarySearch(int[] nums, int key) {
    int low = 0, high = nums.length - 1;
    while (low <= high) {
        int mid = low + (high - low) / 2;
        if (nums[m] == key) {
            return mid;
        } else if (nums[mid] > key) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return -1;
}

总结:

  • 当判断条件使用“nums[i]<=target”时,即将“<”和“==”合在一起执行 low = mid + 1, 最后结果 low 为“含重复target最左边”下标,high 为“比target小的最大的值”;
  • 当判断条件使用“nums[i]>=target”,即将“>”和“==”合在一起执行 high = mid - 1,最后结果 high 为“含重复target最右边”下标,low 为“比target大的最小的值”。
  • 使用变种“<=”+"low=mid"时,(要将 while 条件改为 low<high),最后结果 low 和 high 都为“含重复target最右边”下标;
  • 使用变种“>=”+"high=mid"时,(要将 while 条件改为 low<high),最后结果 low 和 high 都为“含重复target最左边”下标;
原文地址:https://www.cnblogs.com/buptleida/p/12517643.html