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

文章讨论了二分查找在有序数组中寻找目标值的两种不同边界条件处理方法,以及在搜索未找到目标值时,如何确定左端点和右端点的位置。此外,还介绍了原地移除数组中特定值的双指针法,对比了两种不同的实现方式及其优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

704. 二分查找

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

 工作后太久没刷题,二分法逻辑实现上感觉很简单,但实际写起来,因为各种边界条件问题,也是调试了很多遍,最后虽然过了,但总感觉糊里糊涂的。

思路梳理

二分法主要使用场景为在不重复的有序数中查找特定数

二分法原理已有很多文章做过说明在,这里不再赘述。现着重说下边界条件的问题。

取左右端点分别为left,right,中点值mid = (left + right)/ 2。

根据循环终止条件nums[ left] < nums [right] 或是 nums[ left] <= nums [right],有两种写法

写法一

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

写法一中,可以将left和right包含的数组段看作一个左右封闭的数组,即[left, right],包含两端的端点,所以当left==right时,[left,right]存在意义,表示一个点,while中可以写等号。每次缩小范围时,由于mid已经在上一次判断中被比较过,没必要再次重复比较,所以取左右端点时,应该在mid的基础上左右加减1.

写法二

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

写法二中,可以将左右端点包含的数组看作一个左闭右开区间,即[left,right),包含左端点,不含右端点,当left==right,[left,right)数组无意义,所以while内不能取等号。同时,每次取子数组时,由于数组并不包含右端点,所以right的赋值应该直接取mid,如果取mid - 1,则会有值覆盖不到。

拓展题目:

以上题目皆可使用二分查找法完成,这里补充说明一个知识点:

当二分查找未找到值退出时,左端点会指向大于target的最小值,右端点会指向小于target的最大值。

27. 移除元素

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

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

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

 一般涉及到数组的移动,会考虑使用双指针法,一个指针指向当前遍历位置,一个指针定位可移入位置。下面看代码

我自己提交通过代码

    public int removeElement(int[] nums, int val) {
        int len = nums.length;
        int ofset = 0;
        for (int i = 0; i < nums.length; i++){
            while (nums[i] == val ){
                len--;
                i++;
                ofset ++;
                if(i >= nums.length) return len;
            }
            if(ofset > 0){
                nums[i - ofset] = nums[i];
            }
        }
        return len;
    }

 同样时双指针思路,只不过有一个指针是通过ofset偏移值来指定,但是这种写法可读性差,容易出bug,复用性不高。

比较好的写法

    public int removeElement(int[] nums, int val) {
        // 快慢指针
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != val) {
                nums[slowIndex] = nums[fastIndex];
                slowIndex++;
            }
        }
        return slowIndex;
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值