代码随想录算法训练营day01| 704. 二分查找、27. 移除元素

101 阅读3分钟

704. 二分查找

题目描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

题目链接

  • 左闭右闭区间
 class Solution {
     public int search(int[] nums, int target) {
         int start=0,end=nums.length-1;
         int middle=0;
         
         //start和end都指向nums的有效下标,在不断二分过程中,start始终比end小
         //直到start==end,即同时指向同一个位置的元素时,进行最后一次比较
         while(start<=end){
             //start和end的中间
             middle=(start+end)>>1;
             if(nums[middle]==target){
                 return middle;
             }else if(nums[middle]>target){
                 end=middle-1;
             }else{
                 start=middle+1;
             }
         }
 
         return -1;
     }
 }

示意图如下:

  • 情形1:找得到target

二分查找-1.jpg

  • 情形2:找不到target

二分查找-2.jpg

  • 左闭右开区间:
  class Solution {
      public int search(int[] nums, int target) {
          int start=0,end=nums.length;
          int middle=0;
          
          while(start<end){
              middle=(start+end)>>1;
              if(nums[middle]==target){
                  return middle;
              }else if(nums[middle]>target){
                  end=middle;
              }else{
                  start=middle+1;
              }
          }
  
          return -1;
      }
  }

左闭右闭(下面简称为:one:)和左闭右开(下面简称为:two:)的区别:

  1. 循环终止条件不同::one:的循环条件start>=end,:two:的循环条件start>end,因为在:two:中,end所指向的元素不在此次查找范围内,因此查找过程中start!=end
  2. end的移动不同:在查找中,每轮被比较对象nums[middle]在下一软中都不在查找序列中, :one:中end始终指向待查询序列的最后一个位置,因此当end需要移动时,会移动到middle-1的位置;而:two:中end始终指向待查询序列的最后一个位置的下一个位置,此位置必然不在待查询序列中,因此当end需要移动时,移动到middle的位置即可。

27.移除元素

题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

题目链接

暴力解法

思路:

题目要求原地移除元素,因此不能额外借用其他数组或容器来存储结果。

暴力解法对给出的nums数组进行遍历,将每个元素都与val比较,当二者值相同时,将该元素后的所有元素向前移动一格,数组末元素的下标减1。

我们使用end记录数组末尾元素的下标,当发生移动时,即是将[nums[i+1],nums[end-1]]的元素移动到[nums[i],nums[end-2]]中。

在方法返回时,end就是结果数组的长度,返回即可。
一次数组元素的移动如图:

移除元素-暴力解法.jpg

class Solution {
    //暴力方法
    public int removeElement(int[] nums, int val) {

        int end=nums.length;

        for(int i=0;i<end;i++){
            if(nums[i]==val){
                //向前移动[nums[i+1],nums[end-1]]的元素到[nums[i],nums[end-2]]
                for(int j=i+1;j<end;j++){
                    nums[j-1]=nums[j];
                }

                end--;
                i--;
            }
        }

        return end;
    }
}

双指针法

思路:使用快慢指针fastslow对给定数组nums进行操作,其中,fast对数组进行遍历,slow从数组下标为0处依次记录fast在遍历中不为target(代码中该变量为val)的数组元素。当fast完成一次遍历后,slow指向的位置不再记录新的元素,因此,slow的值即为结果数组的长度。

移除元素-双指针法.jpg

class Solution {
    //双指针方法
    public int removeElement(int[] nums, int val) {
        int slow=0,fast=0;

        while(fast<nums.length){
            if(nums[fast]!=val){
                nums[slow]=nums[fast];
                slow++;
            }
            fast++;
        }

        return slow;
    }
}