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

704.二分查找

使用前提:有序数组,且所有元素不重复(元素重复可能导致返回的元素下标不唯一)

思路:依据区间定义这个不变量,在循环过程的边界处理中坚持根据区间的定义来操作

左闭右闭区间

class Solution {
    public int search(int[] nums, int target) {
        
        int left = 0;
        int right = nums.length - 1;
        int middle = 0;

        while(left <= right) {
            middle = left + (right - left) / 2;

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

小结:定义target在一个左闭右闭的区间里,即[left, right]

①while(left <= right):此时left = right是有意义的区间,如[1,1]

②if (nums[middle] > target) right 要赋值为 middle - 1:因为已知nums[middle]肯定大于target,故在下次查找的区间中不会包含middle,那么接下来要查找的左区间结束下标位置就是 middle - 1

左闭右开区间

class Solution {
    public int search(int[] nums, int target) {
       int left = 0;
       int right = nums.length;
       int middle = 0;

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

小结:定义target在一个左闭右开的区间里,即[left, right)

①while(left <= right):此时left = right是没有意义的区间,如[1,1)

②if (nums[middle] > target) right 要赋值为 middle:因为已知nums[middle]肯定大于target,故在下次查找的区间中不会包含middle,那么接下来要查找的左区间结束下标位置就是middle,即:下一个查询区间不会去比较nums[middle]

总结

二分法确实一看就会,一写就废。究其原因在于没有依据一个统一的规则来对边界进行处理,而上述循环不变量的方法则为二分法中的边界处理定义了明确的规则。依据区间定义这个不变量可以清晰地分析并写出二分法中边界处理的代码,而不是通过死记硬背的方式来记住代码。

27.移除元素

思路:题目可以采用暴力解法和双指针解法,两者实际上有异曲同工之妙。双指针解法可以看成是用一层for循环完成了暴力解法中两层for循环所要做的事情。

暴力解法

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

小结:暴力解法的思路很清晰,但在首次实现的时候因为不太熟悉数组的“删除操作”(从前往后依次覆盖元素)以及没有考虑到元素依次往前覆盖后还需将被删除位置的元素向前移动一位,从而导致结果错误。

双指针解法

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;
    }
}

小结:双指针法是指通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。对快慢指针有如下的定义:

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • 慢指针:指向更新新数组下标的位置

快指针向前移动寻找组成新数组的元素,当该元素不是给定的目标元素时,将该元素赋值给新数组中以慢指针为下标的元素,同时慢指针向前移动。当该元素是给定的目标元素时,该元素一定不存在于新数组中,故快指针向前移动。因为此时不需要进行赋值操作,故慢指针不向前移动。重复上述过程直到快指针到达数组的尾部,此时慢指针对应的值则是新数组的元素个数

总结

双指针的正确应用关键在于正确理解双指针的定义,本题通过快慢指针的使用便模拟出了新数组元素的“添加”

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值