二分查找

169 阅读4分钟
  • 基本实现;<=
  • 1.求x的平方根;<=
  • 2.寻找比目标字母大的最小字母;<=
  • 3.有序数组的单一元素;<
  • 4.查找元素在有序数组中的第一个位置和最后一个位置;<=
  • 5.选转排序数组的最小值;<
  • 6.搜索旋转排序数组;<=
  • 7.搜索插入位置;

正常实现

代码:

[0,len-1] l<=h return -1;

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;
}

1. 求x的平方根

二分查找

解法:二分查找
  • 对1-x/2之间的值进行二分,left = 1, right = x/2; while(left <= right) 注意是<=!;
  • 注意mid*mid 的值必须强制转换为long(防止溢出),然后进行比较;
  • 缩小范围的临界点 right = mid-1, left = mid+1;
  • 因为循环条件为left <= right,且返回整数值,所以最终返回right(left > right 终止循环,此时 left*left > 目标值,而right * right < 目标值,选小的数作为结果(向下取整)。

实现:

public int mySqrt(int x) {
        if(x == 0) return 0;
        if(x <= 3) return 1;
        int left = 1;
        int right = x/2;
        while(left <= right){
            int mid = left + (right - left)/2;
            long temp = (long)mid * mid; //需要强制类型转换
            if(temp == x) return mid;
            else if(temp > x){
                right = mid - 1;
            }
            else{
                left = mid + 1;
            }
        }
        return right;
    }

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

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

解法:二分查找

left = 0, right = len; left < right;
如果当前mid值 == target,left = mid + 1;
如果当前mid值 < target, left = mid + 1;
如果当前mid值 > target, right = mid;
返回left%len;(left = right 可能=[1,len])

实现:

 public char nextGreatestLetter(char[] letters, char target) {
        int len = letters.length;

        int left = 0;
        int right = len-1;
        int ans = 0;

        while(left <= right){
            int mid = left + (right - left)/2;
            if(letters[mid] <= target){
                left = mid + 1;
            }
            else{
                right = mid -1;
            }
        }
        return letters[left%len];
            
    }

3.有序数组中的单一元素

有序数组中的单一元素

解法: 二分查找

left = 0, right = len-1; left < right(重点!)
判断mid == mid+1/ mid-1;不等于返回;
如果== mid+1: 通过两边的奇偶判断目标位于哪边;
== mid-1,判断两边长度的奇偶....

 public int singleNonDuplicate(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while(left < right){  //重点
            int mid = left + (right - left)/2;
            if(nums[mid] != nums[mid - 1] && nums[mid] != nums[mid + 1]){
                return nums[mid];
            }
            else if(nums[mid] == nums[mid - 1]){
                if((mid - left - 1)%2 == 0){
                    left = mid + 1;
                }
                else right = mid - 2;
                
            }
            else{
                if((mid - left)%2 == 0){
                    left = mid + 2;
                }
                else right = mid - 1;
            }
        }
        System.out.println(left);
        System.out.println(right);
        return nums[left];
    }

4.查找元素在数组中的第一个位置和最后一个位置

查找第一个位置:

  • left = 0, right = len-1, left <= right;
  • mid > target : right = mid-1;
  • mid == target : right = mid-1; (因为循环结束条件是left>right,所以返回left时可保证不丢失此相等值)
  • mid < target : left = mid+1。
  • return:left < len 且 left == target 时返回left,否则返回-1。
private int firstPos(int[] nums,int target){
        int left = 0;
        int right = nums.length - 1;

        while(left <= right){
            int mid = (left + right)/2;
            if(nums[mid] == target)
                right = mid - 1; //重点
            else if(nums[mid] < target)
                left = mid + 1;
            else if(nums[mid] > target)
                right = mid - 1;
        }

        if(left < nums.length && nums[left] == target) return left;//重点
        else return -1;
    }

查找最后一个位置:

  • left = 0, right = n, left <= right;
  • mid < target : left = mid + 1;
  • mid == target : left = mid + 1; (重点!如果mid是最后一个值则返回的是left-1来保证)
  • mid > target : right = mid - 1;
  • return: return (left - 1 >= 0 && (a[left - 1] == target))? left - 1: -1;
private int lastPos(int[] nums,int target){
    int left = 0;
    int right = nums.length - 1;

    while(left <= right){
        int mid = (left + right)/2;
        if(nums[mid] == target)
            left = mid + 1; //重点
        else if(nums[mid] < target)
            left = mid + 1;
        else if(nums[mid] > target)
            right = mid - 1;
    }

    if(left > 0 && nums[left-1] == target) return left - 1; //重点
    else return -1;
}

5. 旋转排序数组的最小值

旋转排序数组的最小值

  • left = 0, right = len - 1, legt < right(重点!!)
  • 前提: nums[left] < right 则返回left;
  • mid > left: left = mid + 1;
  • mid < right: right = mid;
  • else: left++(left == mid == right情况)
  • return: nums[left]

代码:

public int findMin(int[] nums) {
    int left = 0;
    int right = nums.length - 1;

    while(left < right){ //重点!!!
        int mid = left + (right - left)/2;
        if(nums[left] < nums[right]){
            return nums[left];
        }
        else if(nums[mid] > nums[left]){
            left = mid + 1;
        }
        else if(nums[mid] < nums[right]){
            right = mid;
        }
        else{
            left++;
        }
    }
    return nums[left];
}

6.搜索旋转排序数组

搜索旋转排序数组

  • left = 0, right = len-1, left <= right;
  • mid == target, return;
  • left < mid && 1)[left,<= target,< mid]: right = mid - 1; 2) [mid, < target <= right] left = mid + 1;
  • mid < right && 1)[mid,< target,<= right]: left = mid + 1; 2)[left,<= target,< mid] rigth = mid - 1。
  • (不可能有 = mid情况因为 mid == target已经判断,所以mid一定不是)
public int search(int[] nums, int target) {

    if (nums.length == 0 || nums == null) return -1;

    int length = nums.length;
    int start = 0;
    int end = length - 1;
    while (start <= end) {
        int mid = start + (end - start) / 2;
        if (nums[mid] == target) return mid;
        else {
            if (nums[mid] >= nums[start]) {
                if (nums[start] <= target && target < nums[mid]) {
                    end = mid - 1;
                } else start = mid + 1;
            } else {
                if (nums[mid] < target && target <= nums[end]) {
                    start = mid + 1;
                } else end = mid - 1;
            }
        }
    }
    return -1;
}

7. 搜索插入位置

搜索插入位置