0
点赞
收藏
分享

微信扫一扫

[数组]二分查找

琛彤麻麻 2022-02-12 阅读 57

1、基本思想

前提条件:有序数组+无重复元素(可有可无)
把数组(默认升序)从中间分开,每次与中间值比较,如果小于中间值则只需要找左边,如果大于中间值只需要找右边,等于中间值直接返回下标即可!

2、练习题(建议先去leetcode做,然后再看答案)

  • 704二分查找

// 方式一:左闭右闭[left,right]
class Solution {
    public int search(int[] nums, int target) {
        // 闭区间:r=nums.length-1
        int l = 0;int r = nums.length  - 1;
        // 注意:l<=r
        // 为什么有等于号?
        // [1],只有一个元素的时候,l=0,r=0,m=0,l==r是有意义的
        while (l <= r){
            int m = l + (r - l) / 2;
            if (nums[m] == target) return m;
            else if (nums[m] > target) {
                // 闭区间,所以是r=m-1
                r = m - 1;
            }else{
                l = m + 1;
            }
        }
        return -1;
    }
}

// 方式一:左闭右开[left,right)
class Solution {
    public int search(int[] nums, int target) {
        // 开区间:r=nums.length
        int l = 0;int r = nums.length;
        // [1],只有一个元素的时候,l=0,r=1,m=0,可以比较元素,所以l==r是可以退出的,没有意义的
        while (l < r){
            int m = l + (r - l) / 2;
            if (nums[m] == target) return m;
            else if (nums[m] > target) {
                // 开区间,所以是r=m
                r = m;
            }
            else {
                l = m + 1;
            }
        }
        return -1;
    }
}

  • 35.搜索插入位置
// 方法一:暴力破解
// 遍历数组,查找插入位置
// 如果有此元素则该位置就是插入位置;如果没有此元素,则第一个大于目标元素的数组位置则为插入位置
class Solution {
    public int searchInsert(int[] nums, int target) {
        for (int i = 0; i < nums.length; i ++) {
            if (nums[i] >= target)  return i;
        }
        return nums.length;
    }
}
// 方法二:二分查找
// 如果数组中存在此元素,利用二分找到此元素位置返回
// 如果没有此元素,则r的位置正好是小于目标元素的位置,则r+1正是需要插入的位置
class Solution {
    public int searchInsert(int[] nums, int target) {
        int l = 0; int r = nums.length - 1;
        while (l <= r){
            int m = l + (r - l) / 2;
            // 等于某个元素
            if (nums[m] == target) return m;
            else if (nums[m] > target) {
                r = m - 1;
            }else{
                l = m + 1;
            }
        }
        // 最左边
        // 最右边
        // 某个元素后面
        return r + 1;
    }
}

  • 34.在排序数组中查找元素的第一个和最后一个位置
// 方法一:二分查找
// 首先通过二分查找到该元素位置(因为有重复元素找到的位置不一定是最左边或者最右边),然后左边滑动指针找到最左边,右边滑动直至找到最右边
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int index = binarySearch(nums, target);
        if (index == -1) {
            return new int[]{-1, -1};
        }

        // 左滑指针
        int left = index;
        while (left - 1 >= 0 && nums[left - 1] == target){
            left = left - 1;
        }
        // 右滑指针
        int right = index;
        while (right + 1 < nums.length && nums[right + 1] == target){
            right = right + 1;
        }
        return new int[]{left, right};
    }
    public int binarySearch(int[] nums, int target){
        int l = 0; int r = nums.length - 1;
        while (l <= r){
            int m = l + (r  - l) / 2;
            if (nums[m] == target) return m;
            else if (nums[m] > target) r = m - 1;
            else l = m + 1;
        }
        return -1;
    }
}
// 方法二:二分查找
// 如果元素存在的话,通过二分查找找到左右边界
// 如果元素不存在直接返回{-1,-1}
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
		// 只有任何一个边界没有找到就是没有此元素
        if (rightBorder == -2 || leftBorder == -2){
            return new int[]{-1, -1};
        }
		// 左右边界找的是开区间,只要存在肯定是间隔大于1
        if (rightBorder - leftBorder > 1){
            return new int[]{leftBorder + 1, rightBorder - 1};
        }
        return new int[]{-1, -1};

    }
    // 找到左边界,此左边界是需要+1的
    // 左边界是从右边一步一步找到第一个小于目标值的位置
    public int getLeftBorder(int[] nums, int target){
        int l = 0; int r = nums.length - 1;
        int leftBorder = -2;
        while (l <= r){
            int m = l + (r - l) / 2;
            // 如果中间值大于等于目标值,则r = m - 1;同时更新左边界的值
            if (nums[m] >= target){
                r = m - 1;
                leftBorder = r;
            }else{
                l = m + 1;
            }
        }
        return leftBorder;
    }
    // 找到右边界,此右边界是需要-1的
    // 右边界,是从右边找到第一个大于目标值的位置
    public int getRightBorder(int[] nums, int target){
        int l = 0; int r = nums.length - 1;
        int rightBorder = -2;
        while (l <= r){
            int m = l + (r - l) / 2;
            if (nums[m] > target){
                r = m - 1;
            }else{
                // 如果中间值小于等于目标值,则l = m + 1;同时更新右边界的值
                l = m + 1;
                rightBorder = l;
            }
        }
        return rightBorder;
    }
}

  • 69.x 的平方根
// 方法一:暴力破解
// 遍历x,知道找到第一个数的平方大于目标值
class Solution {
    public int mySqrt(int x) {
        // x = 0特殊情况
        int res = 0;
        // x = 1特殊情况
        if (x == 1) res = 1;
        for (long i = 1; i <= x / 2; i++){
            long tmp = i * i;
            if (tmp > x){
                res = (int)i - 1;
                break;
            }else{
                res = (int)i;
            }
        }
        return res;
    }
}
// 方法二:二分查找
// 从0到x,通过二分查找,如果某个数的平发正好等于x则直接返回
// 如果没有直接等于的,则r就是最接近的整数值
class Solution {
    public int mySqrt(int x) {
        int l = 0; int r = x;
        while (l <= r){
            long m = l + (r - l) / 2;
            long tmp = m * m;
            if (tmp == x){
                return (int)m;
            }else if (tmp > x){
                r = (int) m - 1;
            }else{
                l = (int) m + 1;
            }
        }
        return r;
    }
}

  • 367.有效的完全平方数
// 二分查找
// 从0到num,如果某个数的平发正好等于num,则返回true,否则返回false
class Solution {
    public boolean isPerfectSquare(int num) {
        int l = 0; int r = num;
        while (l <= r){
            long m = l + (r - l) / 2;
            long tmp = m * m;
            if (tmp == num){
                return true;
            }else if (tmp > num){
                r = (int) m - 1;
            }else{
                l = (int) m + 1;
            }
        }
        return false;
    }
}


举报

相关推荐

0 条评论