leetcode34.在排序数组中查找元素的第一个和最后一个位置——学习笔记

题目:力扣icon-default.png?t=LBL2https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if(nums.length==1){
            if(nums[0]==target){
                return new int[]{0,0};
            }else{
                return new int[]{-1,-1};
            }
        }else if(nums.length==2){
            if(nums[0]==target && nums[1]==target){
                return new int[]{0,1};
            }else if(nums[0]!=target && nums[1]==target){
                return new int[]{1,1};
            }else if(nums[0]==target && nums[1]!=target){
                return new int[]{0,0};
            }else{
                return new int[]{-1,-1};
            }
        }
        int left = 0;
        int right = nums.length-1;
        int l = -1;
        int r = -1;
        //先找left
        while(left<right){
            int mid = (left+right)/2;
            if(nums[mid]==target){
                right = mid;
            }else if(nums[mid]>target){
                right = mid-1;
            }else if(nums[mid]<target){
                left = mid+1;
            }
            if(nums[left]==target){
                l = left;
            }
        }
        //再找right;
        right = nums.length-1;
        while(left<=right){
            int mid = (left+right)/2;
            if(nums[mid]==target){
                left = mid;
            }else if(nums[mid]>target){
                right = mid-1;
            }else if(nums[mid]<target){
                left = mid+1;
            }
            
            if(nums[right]==target || right-1>=0 && nums[right-1]==target){
                r = (((left+right)/2)<right && nums[right]!=target)?right-1:right;
                break;
            }
        }
        if(l!=-1){
            return new int[]{l,r};
        }else{
            return new int[]{-1,-1};
        }
    }
}

 

 思路:这题一开始一直超时,没想到只要AC了就超于100%了哈哈哈,挺开心的。一开始一直超时,其中很重要的一个原因是我使用了线性查找,哪怕只是局部使用。题目要求时间复杂度是O(\log n),一开始我并没有留意这个点。我最初的思路是,二分查找+线性查找,但是只要一用到线性查找时间复杂度就不符合题目要求。因此,只能一直用二分查找直至找到答案。

1.特殊情况一:数组长度为1。只需要判断这个元素是否为target就好,是则返回[0,0],否则返回[-1,-1]。

if(nums.length==1){
    if(nums[0]==target){
        return new int[]{0,0};
    }else{
        return new int[]{-1,-1};
    }
}/*else if(){
    //......
}*/

2.特殊情况二:数组长度为2。仅仅有四种情况,全部一一枚举出来。若数组中两个位置的数都与target不同,则返回[-1,-1];若仅第一个位置的数与target相同,则返回[0,0];若仅第二个位置的数与target相同,则返回[1,1];若数组中两个位置的数都与target相同,则返回[0,1]。

if(nums.length==2){
    if(nums[0]==target && nums[1]==target){
        return new int[]{0,1};
    }else if(nums[0]!=target && nums[1]==target){
        return new int[]{1,1};
    }else if(nums[0]==target && nums[1]!=target){
        return new int[]{0,0};
    }else{
        return new int[]{-1,-1};
    }
}

3.接下来就是正式开始操作了。left和right是左右边界,l和r是最后存储答案的变量先赋值为-1。

int left = 0;
int right = nums.length-1;
int l = -1;
int r = -1;

4.先确定左边界l的值。利用二分法查找,确定左边界的值。首先,先求中间位置mid的值(这个中间位置不一定是严谨的,后续会详细说明)。若中间位置的值等于target,此时有两种情况有可能发生(1)此处已经是左边界(2)左边界在此处的左侧,这两种情况我们此时仍然不能确定,所以需要将右边界移动到中间位置,继续进行判断。直到left和right重合时,既可以找出左边界,用变量l储存。

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

5.然后再确定右边界r的值。仍然是利用二分法查找右边界,具体方法与第4步相似。求右边界有一个难点,与求左边界有一个不同。因为int类型没有小数,当除2时出现小数会自动向下取整,所以在求左边界时会很顺利,而在求右边界时自动向下取整会出现“卡死”现象(例如:left为1,right为2,1+2=3,3/2=1,因为"letf = mid;"所以left又为1,mid的值也一直为1,一直无限循环下去......)此时我们需要另外作一个判断,我用的时三目运算符a=b?c:d;这个符号的意思时,若b为true则将c的值赋值给a,若为false则将d的值赋值给a。回到题目,若(left+right)/2小于right且nums[nums]不等于target,则出现了“卡死”现象,我们将right-1赋值给r;否则,即不是“卡死”现象,直接将right赋值给r即可。最后补个break,避免出现“卡死”一直在此处死循环。

right = nums.length-1;
while(left<=right){
    int mid = (left+right)/2;
    if(nums[mid]==target){
        left = mid;
    }else if(nums[mid]>target){
        right = mid-1;
    }else if(nums[mid]<target){
        left = mid+1;
    }
    if(nums[right]==target || right-1>=0 && nums[right-1]==target){
        r = (((left+right)/2)<right && nums[right]!=target)?right-1:right;
        break;
    }
}

6.选择正确的值返回。来到这一步,证明已经走过了上面最难的一步了。若上述代码已经找到了左右边界,则l和r的值肯定已经改变了;若不存在左右边界,则按题意返回(-1,-1)即可。

if(l!=-1){
    return new int[]{l,r};
}else{
    return new int[]{-1,-1};
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hokachi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值