二分查找-Java【算法学习day.03】

一.概念
二分查找法,它充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用O(log n)完成搜索任务。它的基本思想是:(这里假设数组元素呈升序排列)将n个元素分成个数大致相同的两半,取a[n/2]与欲查找的x作比较,如果x=a[n/2]则找到x,算法终止;如 果x<a[n/2],则我们只要在数组a的左半部继续搜索x;如果x>a[n/2],则我们只要在数组a的右 半部继续搜索x。
二.在题目中掌握思想(代码中会有注释)
ps:题目均选自力扣

1.二分查找
题目链接:704. 二分查找 - 力扣(LeetCode)
在这里插入图片描述
基本分析:可以通过遍历一遍查找target,这样时间复杂度是O(n),而二分是O(logn),而在二分的基础使用中应该注重的就是边界问题,常见的是左闭右开和左闭右闭,什么时候l<=r,什么时候l<r,什么时候l=m+1等等。下面会有两版代码分别讲解左闭右开和左闭右闭

左闭右闭:

class Solution {
    public int search(int[] nums, int target) {
        int l = 0;//定义左边界
        //定义右边界,此时我们是以nums.length-1为右边界,
        //那么我们是认为右边界是有意义的,请牢记这一点
        int r = nums.length-1;
        int flag = -1;//用来标记目标的索引
        while(l<=r){//因为右边界是有意义的,所以i可以等于j
            int m = (l+r)>>>1;//相当于(i+j)/2,在java中整数间的除法是向下取整
            //nums[m]>target,则索引m右边的元素都大于target,由于m不可能是target且j有意义,所以j=m-1
            if(nums[m]>target)r = m-1;
             //nums[m]<target,则索引m左边的元素都小于target,由于m不可能是target且i有意义,所以i=m+1
            else if(nums[m]<target) l=m+1;
            //相等,则是目标元素,标记并跳出循环
            else {
                flag = m;
               break;
            };
        }
        return flag;
    }
}

左闭右开:

class Solution {
    public int search(int[] nums, int target) {
        int l = 0;//定义左边界
        //定义右边界,此时我们是以nums.length为右边界,
        //由于右边界超出了数组索引范围,那么我们是认为右边界是没有意义的,请牢记这一点
        int r = nums.length;
        int flag = -1;//用来标记目标的索引
        while(l<r){//因为右边界是没有意义的,所以i不等于j
            int m = (l+r)>>>1;//相当于(i+j)/2,在java中整数间的除法是向下取整
            //nums[m]>target,则索引m右边的元素都大于target,由于m不可能是target且j没有意义,所以j=m
            if(nums[m]>target)r = m;
             //nums[m]<target,则索引m左边的元素都小于target,由于m不可能是target且i有意义,所以i=m+1
            else if(nums[m]<target) l=m+1;
            //相等,则是目标元素,标记并跳出循环
            else {
                flag = m;
               break;
            };
        }
        return flag;
    }
}

现在你应该对二分有了一个基本的认识了
2.在排序数组中查找元素的第一个和最后一个位置
题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
在这里插入图片描述
基本分析: 在左闭右闭中,如果我把判断条件改为nums[m]>=target,r=m-1亦或者nums[m]<=target,l=m+1会发生什么变化?

我们就拿nums[m]>=target,r=m-1来讲述,则循环条件中代码如下:

  while(l<=r){
        int m = (l+r)>>>1;
        if(nums[m]>=target)r=m-1;
        else l = m+1;
     } 

上述代码:我们假设循环来到了最后一步,也就是l=r=m,目标元素(可以是多个)存在于数组内我们分为两种情况:

    a.是因为l=m+1导致循环跳出

    如果走的是第一个条件,也就是nums[m]>=target,由于目标值存在于数组内,什么情况下不走第一个?也就是上一步中nums[m]==target,然后走了第一个条件了。那我们就可以推断出m+1就是目标元素最左边的所以,由于l+1了,所以就是l;

    b.是因为r=m-1导致循环跳出

    说明走的是第一个条件,由于是最后一步,不可能存在m索引左边存在元素大于等于target,因为此时i都等于m了,也就是说,最后一步是nums[m]==target,m就是目标元素最左边的位置,所以也就是l;
    思想知道了,代码如下:
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] arr2 = { -1, -1 };
        if (nums.length == 0)
            return arr2;
        int i1 = 0, i2 = 0;
        int j1 = nums.length - 1, j2 = nums.length - 1;
        while (i1 <= j1) {
            int m = (i1 + j1) >>> 1;
            if (nums[m] >= target)
                j1 = m - 1;
            else
                i1 = m + 1;
        }
        if (!(i1 <= j2 && nums[i1] == target))
            return arr2;
        arr2[0] = i1;
        while (i2 <= j2) {
            int m = (i2 + j2) >>> 1;
            if (nums[m] <= target)
                i2 = m + 1;
            else
                j2 = m - 1;
        }
        arr2[1] = j2;
        return arr2;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值