【算法系列-数组】二分查找

【算法系列-数组】二分查找

1. 算法分析

二分查找这类算法题大家并不陌生,对于它的使用方式主要有两个难点,也是最容易出错的地方,就是分不清边界值

LeetCode:二分查找

一般二分查找分为两个场景:

  • 左闭右闭区间,如:提供数组[1, 5]
  • 左闭右开区间,如:提供数组[1, 5)

对此,只需要分清楚在什么场景下使用合适的边界值就能很好的写出这类题,接下来帖上两段代码:

  1. 左闭右闭

    class Solution {
        public int search(int[] nums, int target) {
            int left = 0;
            int right = nums.length -1;
            while (left <= right) { // 这里使用 `<=` ,因为在左闭右闭区间中,left = right 是合理逻辑,对此需要加上等号
                int mid = left + (right - left) / 2;
                if (nums[mid] > target) {
                    right = mid -1; // 因为此处 nums[mid] 不等于目标值,不需要取到mid,所以在左闭右闭区间 right = mid - 1
                }
                else if (nums[mid] < target) {
                    left = mid + 1;
                }
                else {
                    return mid;
                }
            }
            return -1;
        }
    }
    
  2. 左闭右开

    class Solution {
        public int search(int[] nums, int target) {
            int left = 0;
            int right = nums.length -1;
            while (left < right) { // 这里使用 `<` ,因为在左闭右开区间中,left = right 是不合理逻辑,对此无需加上等号
                int mid = left + (right - left) / 2;
                if (nums[mid] > target) {
                    right = mid; // 因为此处 nums[mid] 不等于目标值,不需要取到mid,而右开区间不取右值,所以right = mid,假设使用 mid - 1,则此时 mid - 1 的值将不会被取到,从而产生问题
                }
                else if (nums[mid] < target) {
                    left = mid + 1;
                }
                else {
                    return mid;
                }
            }
            return -1;
        }
    }
    

掌握解题思路后,以下几道相同类型的题可用来练习:

2. 搜索插入位置(LeetCode 35)

【题目链接】

2.1 解题思路

二分法能够解决这个问题,最主要的是在值不存在的情况下二分法得出的mid让我们节省来再去循环遍历的过程

2.2 解题过程

二分查找流程

先定义左右下标,将mid放在循环外面创建,因为当前数组为闭区间(左闭右闭),所以while循环使用left <= right 合理(如[1,1]),之后,当中间值大于目标值时,右下标 = mid - 1;当中间值小于目标值时,right = mid + 1;当中间值等于目标值时,返回mid;

查无目标值

当循环结束还没有找到目标值时,只有三种情况:

  1. right小于0,表示目标值小于数组中的所有值,返回left(此时left = 0);
  2. left大于数组长度,表示目标值大于数组中的所有值,返回left(此时left = num.length);
  3. 以上两者都不符合表示目标值需在数组中插入,此时right一定在left的左边才能退出循环,返回left(即left的位置即是插入位置)

综上所述,查无目标值的情况返回left即可

2.3 代码

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

3. 在排序数组中查找元素的第一个和最后一个位置(LeetCode 34)

【题目链接】

3.1 解题思路

二分查找+递归实现

3.2 解题过程

这道题的难点在于数组中可能存在多个相同的目标值,我们通过二分查找找到一个目标值后还需要进行判断,找到第一个和最后一个相同的目标值的下标,对此可以通过递归的方式来解决:

在找到一个目标值后,依次向左向右查询相同值

  • 向左递归:传入数组与当前目标值下标mid,判断 mid - 1 位置的值是否与当前目标值相同,若相同则传入mid - 1继续递归,否则返回当前目标值的下标**mid,**此时的mid即目标值在数组中的第一个位置;
  • 向右递归:传入数组与当前目标值下标mid,判断 mid + 1 位置的值是否与当前目标值相同,若相同则传入mid + 1继续递归,否则返回当前目标值的下标**mid,**此时的mid即目标值在数组中的最后一个位置;

最后返回结果赋值即可

3.3 代码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int ret[] = new int[2];
        ret[0] = -1;
        ret[1] = -1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > target) {
                right = mid - 1;
            }
            else if (nums[mid] < target) {
                left = mid + 1;
            } 
            else {
                ret[0] = searchLeft(nums, mid);
                ret[1] = searchRight(nums, mid);
                return ret;
            }
        }
        return ret;
    }

    public int searchLeft(int[] nums, int mid) {
        
        if (mid - 1 >= 0 && nums[mid - 1] == nums[mid]) {
            mid = searchLeft(nums,  mid - 1);
        }
        return mid;
    }

    public int searchRight(int[] nums, int mid) {
        
        if (mid + 1 < nums.length && nums[mid + 1] == nums[mid]) {
            mid = searchRight(nums,  mid + 1);
        }
        return mid;
    }
}

4. X的平方根(LeetCode 69)

【题目链接】

4.1 解题思路

因为这道题限制了我们使用api,所以可以通过二分查找的方式来解决

4.2 代码

class Solution {
    public int mySqrt(int x) {
        int left = 0;
        int right = x;
        int ret = -1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if ((long) mid * mid <= x) {
                ret = mid;
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        return ret;
    }
}

5. 有效的完全平方数(LeetCode 367)

【题目链接】

5.1 解题思路

与上道题很相似,只是返回的结果不同了,稍微调整即可

5.2 代码

class Solution {
    public boolean isPerfectSquare(int num) {
        int left = 0;
        int right = num;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if ((long) mid * mid < num) {
                left = mid + 1;
            }
            else if ((long) mid * mid > num) {
                right = mid - 1;
            }
            else {
                return true;
            }
        }
        return false;
    }
}

以上便是对二分查找的介绍了!!后续还会继续分享其它算法系列内容,如果这些内容对大家有帮助的话请给一个三连关注吧💕( •̀ ω •́ )✧( •̀ ω •́ )✧✨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值