代码随想录/704二分查找/27移除元素

本文详细解析了二分查找算法的工作原理,包括如何根据区间选择(左闭右闭/左闭右开)更新边界值,处理数据溢出,以及在特定问题如移除元素、搜索插入位置和查找元素范围的应用。同时介绍了两种不同情况下的左边界处理策略。
摘要由CSDN通过智能技术生成

704.二分查找

题目链接

题目分析

二分查找就是类似于对半分,把一个有序的(升序)且无重复元素的数组分成两段,数组第一个元素下标设为left,数组最后一个元素的下标设为right,分的时候中间元素的下标设置为mid,mid=left+((right-left)>>1).相当于把一个数组分成两段,当目标元素在第一段之间时,就把第二段舍弃(并不是真的删除,而是不在这个区间内查找了),然后再把第一段分成两段,思路同上,在前一段之间就把不在的那段舍弃,然后均分剩下的那段,直到找到目标元素。

这个题的主要讨论点在于更新左右边界值,比如目标元素大于中间值mid,那证明目标元素在第二段,也就是在[mid,right],这段,那就要把[left,mid]这段舍弃掉,然后重新均分[mid,right]这段,这就要更新left值,将mid的值赋值给left,但是在代码实现是到底是让 left=mid 还是让 letf=mid+1 这就要分情况最开始所定义的区间到底是左闭右闭还是左闭右开。同时区间的选择也确定了进入循环的条件到底是left<=right,还是left<right.

左闭右闭:
left=0; right=nums.length-1
[1,1]这个区间是合法的,所以left<=right是合法的,所以在这个区间里进入循环的条件是left<=right。又因为right是可以取到的,那么mid也是可以取到的,如果目标元素大于中间值mid,那么就证明mid肯定也小于目标值,所以可以和第一阶段一起舍弃掉,所以letf=mid+1,right = mid-1

左闭右开
left=0; right=nums.length
[1,1)如果left<=right 那么这个区间就是不合法的,因为既不能又等于1,又取不到1,所以进入循环的条件是left<right。又因为right是取不到的,所以mid也是取不到的,如果目标元素小于中间值mid,那么right = mid,现在就是[left,mid)区间,当目标元素大于mid时取右半段,那么就是[mid+1,right)区间,加一是因为本身mid就取不到,+1后才能取得准确的一个值

还有一个数据溢出问题,当left right都很大的时候,再相加可能就太大了超出了 INT_MAX,就会造成数据溢出的问题,这时候,我们就mid = left + ((right - left) >> 1)就可以了,left + (right - left)/2<=right 所以这样就不会溢出。对于二进制的正数来说,右移x位相当于除以2的x几次方,所以右移一位(>>1)等于➗2,用位运算的好处是比直接相除的操作快。right - left:这部分计算了数组的长度,也就是右边界和左边界之间的距离。((right - left) >> 1):这是将上述长度除以2的操作,使用位运算 >> 1 来实现。位运算的右移操作相当于将一个数除以2的n次方(这里是2的1次方,即除以2),因此它有效地将长度除以2,得到中间位置距离左边界的偏移量。left + ((right - left) >> 1):最后,将偏移量加上左边界 left,得到了中间索引 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-left)>>1);
            if(nums[mid] < target){
                left = mid+1;
            }else if(nums[mid]>target){
                right = mid-1;
            }else{
                return mid;
            }
        }
        return -1;
}
}

2.左闭右开

class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        while(left<right){
            int mid = left + ((right-left)>>1);
            if(nums[mid] < target){
                left = mid+1;// target 在右区间,在[middle + 1, right)中
            }else if(nums[mid]>target){
                right = mid;// target 在左区间,在[left, middle)中
            }else{
                return mid;
            }
        }
        return -1;
}
}

27.移除元素

题目链接

双指针:

定义两个指针,一个快指针,一个慢指针。就相当于两个平行空间,在第一个空间是快指针,它用来遍历数组查找要移除的元素,第二个空间是慢指针,它用来接收合法的元素,然后赋值就相当于两个空间产生了交集。这样虽然是两套循环遍历,但都是操作在一个数组上,所以只用一个循环就可以。
1.双指针法

class Solution {
    public int removeElement(int[] nums, int val) {
        int j=0;//快指针
        int i=0;//慢指针
     for(i=0,j=0;i<nums.length;){
         if(nums[i]!=val){//快指针所指向的值不等于要移除的值
             nums[j] = nums[i];//将合法的值赋值给慢指针所指的数组
             j++;
             i++;
         }else{
            i++; 
         }  
     }
     return j;
    }
}

2.暴力算法
两套循环,第一层循环找到要移除元素,第二层循环覆盖要移除的元素


class Solution {
    public int removeElement(int[] nums, int val) {
        int size = nums.length;
        for(int i=0;i<size;i++){
            if(nums[i]==val){
                for(int j=i+1;j<size;j++){
                    nums[j-1]=nums[j];
                }
            i--;
            size--;
            }
        }
        return size;
    }
}

35.搜索插入位置

题目链接

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

分析

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。如果找不到,那就返回的是第一个大于目标值的元素的下标。首先nums为无重复元素的升序数组(题给的提示)所以首先考虑二分法。这个题就是找到第一个大于目标元素的值的下标,正常来讲,二分法所返回的找到的值就是mid,那么第一个大于的就是mid+1,就是left.

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

题目链接

分析

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。要求时间复杂度,且时有序数组,首先想到二分查找。
两次二分查找 第一次找左边界,第二次右边界。
左边界:当mid=target时,如果mid-1<target或者mid=0时代表着开始边界,如果不是那代表着开始边界在mid的左边,那就让right=mid-1。然后继续二分查找。
右边界:当mid=target时,如果mid+1<target或者mid=nums.length-1时代表着结束边界,如果不是那代表着结束边界在mid的右边,那就让left=mid+1。然后继续二分查找。

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int up=leftBonder(nums,target);
        int low=rightBonder(nums,target);
        return new int[]{up,low};
    }

//找左边界,左边的都小于目标值,就是说找最后一个小于目标值的
        int leftBonder(int[] nums,int target){
           int left=0;
        int right=nums.length-1;
        
        while(left<=right){
            int mid = left+((right-left)>>1);
            if(nums[mid]==target){
                if(mid==0||nums[mid-1]<target){
                    return mid;
                }
                right = mid-1;
            }else if(nums[mid]>target){
                right=mid-1;
            }else if(nums[mid]<target){
                left = mid+1;
            }
           
        } 
         return -1;
        }
         //找右边界,右边的都大于目标值,就是说找第一个大于目标值的
        int rightBonder(int[] nums,int target){
             int left=0;
        int right=nums.length-1;
             while(left<=right){
            int mid = left+((right-left)>>1);
            if(nums[mid]==target){
                if(mid==nums.length-1||nums[mid+1]>target){
                    return mid;
                }
                left = mid+1;
            }else if(nums[mid]>target){
                right=mid-1;
            }else if(nums[mid]<target){
                left = mid+1;
            }
            }
            return -1;
        }
        
}    

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值