代码随想录算法训练营第一天| 704二分查找,27移除元素,35搜索插入位置,34

704. 二分查找

数组需要两点注意的是

  • 数组下标都是从0开始的。
  • 数组内存空间的地址是连续的

解法1 左闭右闭

有效区间是【left,right】,所以当经过一次if-else判断后不能包含nums[mid](因为它已经参与过判断),所以应该是left=mid+1; right=mid-1;

class Solution {
    public int search(int[] nums, int target) {
        int left=0;
        int right=nums.length-1;
        
        while(left<=right){
            int   mid=(left+right)>>>1;

            if(nums[mid]<target){//目标值在右边
            left=mid+1;//左闭右闭,说明mid已经是被搜索过的,因此不要包含

            }
            else if(nums[mid]>target){
                right=mid-1;

            }
            else {
                return mid;
            }
           
        }
        return -1;

    }
}

解法2 左闭右开

有效区间是【left,right),当target在下标mid的右边时,始终牢记左闭,说明mid是被判断过的(这个逻辑参考解法1),故 left=mid+1;当target在下标mid的左边时,始终牢记右开,说明mid是没有被判断过的,故right=mid

class Solution {
    public int search(int[] nums, int target) {
        int left=0;
        int right=nums.length;
        
        while(left<right){
            int   mid=(left+right)>>>1;

            if(nums[mid]<target){//目标值在右边,【left,right)
              left=mid+1;//左闭右开,说明mid是被搜索过的,因此不要包含

            }
            else if(nums[mid]>target){//目标值在左边
                right=mid;//左闭右开,说明mid是没被搜索过的,要包含

            }
            else {
                return mid;
            }
           
        }
        return -1;

    }
}

总结:开头对有效区间的定义,既决定了while循环里的条件也决定了left和right的变化

27. 移除元素

双指针解法,之前做过,今天脑袋抽风居然没写对,我还是基础不扎实啊!

关键点是fast指针每次判断,当遇到nums[fast]!=val时slow指针才会移动

class Solution {
    public int removeElement(int[] nums, int val) {
        //你不需要考虑数组中超出新长度后面的元素。
        //双指针
        int slow=0;
        
        for(int fast=0;fast<nums.length;fast++){
            if(nums[fast]!=val){
                nums[slow]=nums[fast];
                slow++;
        
            }
          

        }

        return slow;

    }
}
 35. 搜索插入位置

依旧是三种情况,要找的是插入位置,类似于找一个边界问题的,搜索区间左闭右闭

因此最后退出循环的情况应该是right=left-1;就是right指针回在left的右边,因此这里返回left或者right+1都是可以的

class Solution {
    public int searchInsert(int[] nums, int target) {
       
        //寻找一个左边界的问题,nums 为 无重复元素 的 升序排列数组
     
        int left=0;
        int right=nums.length-1;
        while(left<=right){
            int mid=(left+right)>>>1;
            if(nums[mid]<target){//
              left=mid+1;

            }
            else if(nums[mid]>target){//
             right=mid-1;
            }
            else{//目标值等于数组中的某一个元素
            return mid;

            }
            
        }
        //最后的退出循环的情况应该是right=left-1;就是right指针回在left的右边,因此这里返回left也是可以的
        return left;


    }
}

 解法2 这是我做完34题回来看35题,其实35题就是寻找左边界的问题

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

            }
            else if(nums[mid]<target){
                left=mid+1;
            }
           
        }
        return right+1;

    }
}


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

思路:需要寻找到右边界和左边界,在寻找左边界时,如果如果当前nums[mid]==target,就需要再进一步看看nums[mid-1]还是不是target,因此 if(nums[mid]>=target){//寻找左边界这里需要等号;

在寻找右边界时,只有if(nums[mid]>target)遇到大于的情况,才会移动右边界,因此,最终right一定指向target,但是left一定是指向右边界的右一位元素
举个例子

“3 4 5 8 8 9”

class Solution {
    public int[] searchRange(int[] nums, int target) {
        //二分法查找
        int leftIdx=leftBorder(nums,target);
        int rightIdx=rightBorder(nums,target)-1;//注意这里的减1
        System.out.println(leftIdx);
        System.out.println(rightIdx);
        if(leftIdx<=rightIdx&&rightIdx<nums.length&&nums[leftIdx]==target&&nums[rightIdx]==target){
            return new int[]{leftIdx,rightIdx};

        }
         return new int[]{-1,-1};


    }
    //找左边界
    public int leftBorder(int[] nums, int target){
        //
        int left=0;
        int right=nums.length-1;
        int ans=0;

        while(left<=right){
            int mid=(left+right)>>>1;
            if(nums[mid]>=target){//寻找左边界
                right=mid-1;
                ans=mid;//可能是要找的左边界
            }
            else{
                left=mid+1;

            }

        }
         return ans;

    }
       //找右边界
    public int rightBorder(int[] nums, int target){
        //
        int left=0;
        int right=nums.length-1;
        int ans=nums.length;

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

            }

        }
        System.out.println(ans);
         return ans;

    }
   
}

注意点

开始我把ans初始化为0,在用例nums:[1],target=1卡住了,

然后把寻找右边界的地方ans=nums.length就AC了,寻找左边界还是ans=0;

原因是在寻找右边界时,本身nums[mid]==target了,直接将left=1然后退出循环了,返回的是0,但是在主函数中对右边界减去1,所以最终的 leftIdx=0,rightIdx=-1,必然出错

所以我们在寻找右边界方法时要把ans设置成nums.length

在做完这道题后回去看35题其实是一样的思路

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值