代码随想录刷题Day1


704-二分法

二分法是一种针对有序序列效率很高的查找方法,但是在写的时候要注意边界条件,要保持在每次循环时区间的定义都一致,即要么一直是“左闭右闭”型区间,要么一直是“左闭右开”型区间。

我自己比较熟悉的是左闭右闭型区间:

class Solution {
    public int search(int[] nums, int target) {
        // 左右指针均是指向实实在在的元素,即“左闭右闭”型区间
        int left = 0, right = nums.length-1;
        while(left <= right){
            int middle = (right -left) / 2 + left;
            if(nums[middle] > target){
                right = middle-1;
            }else if(nums[middle] < target){
                left = middle + 1;
            }else{
                return middle;
            }
        }
        return -1;
    }
}

但是卡哥要求要同时熟练掌握“左闭右闭”和“左闭右开”型区间的写法,没办法,硬着头皮写吧,让自己的大脑习惯学习新东西!

class Solution {
    public int search(int[] nums, int target) {
        // 这种情况下,右指针指向的是一个不包括的元素,即“左闭右开”型区间
        int left = 0, right = nums.length;
        while(left < right){
            int middle = (right - left) / 2 + left;
            int temp = nums[middle];
            if(target > temp){
                left = middle + 1;
            }else if(target < temp){
                right = middle;
            }else{
                return middle;
            }
        }
        return -1;
    }
}

27-移除元素

本题是一道双指针法的一个入门题目,但是卡哥又觉得说先暴力解决,有助于提升代码能力,我怎么能放弃这个提升自己的机会呢?哈哈,开干吧!暴力实现之后才发现没有那么简单。

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

此题中n尤为关键,它用于记录数组中和val值相等的元素个数,这个还用于设置边界条件,一旦发现一个和val值的元素,本质上数组的长度会减少1,因此第一层循环和第二层循环的边界不再是无脑的nums.length了,(别问为什么,问就是一开始我无脑设置边界为nums.length,最后会成为一个死循环

还有一个就是i不是一直都默认增长的,当发现和val值相等的元素时,会从i开始移动数组元素,i位置的元素会发生变化,因此也要进行判断,只有不和val值相等时才会默认增加i


暴力法解决之后,进入正题,使用“双指针法”进行移除数组元素

先上代码:

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

我的理解是:设置两个指针back, front,一开始指向数组第一个元素,然后一起遍历,如果遇到和val值相同的元素,这是back停下来指向它,front向前走; 否则用front指向的元素覆盖back所指向的元素,此时back与front同时向前移动。 直到front到达数组末尾,结束循环。此时数组的个数就是back的值。


扩展题目:

35-搜索插入位置

同样是二分法的应用,只不过这次不保证数组中一定能找到指定元素,如果找不到,则返回其应该插入的位置。同样使用二分法查找,只是再找不到的时候返回left的值即可。

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

为什么返回left呢?换种思维想一下:比如再 1 3 4 5 中找2,显然找不到,但是我们知道2应该插在1和3之间,必定最后有一个情况是left=1, right=3, 这时取中midlle显然是left,而2比nums[middle]大,因此left会被加一,然后结束,因此left的值刚好是应该2插入的位置

其实这个时候在我看来和计算机的特性相关了,因为取整是直接去掉浮点数部分,再加上二分法的一个特性,所以left就是插入位置。有时候不是所有东西都能想通的,接受它,接纳它,使用它即可!

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

这也是二分法的一个变种题,难度又有了一点提升。
现在的情形是:排序数组中有可能有重复出现的数,需要找出出现的第一个位置和最后一个位置,第一次看到这个题目我其实是很懵逼的,但是后面仔细一想,使用二分法找到第一个元素后,继续更新边界值,重新寻找,直到找到第一个位置和最后一个位置。

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0, right = nums.length-1;
        int[] res = new int[2];
        // 默认不存在
        res[0] = -1;
        res[1] = -1;
        // 找最左边出现的位置
        while(left <= right){
            int middle = (right -left) / 2 + left;
            int temp = nums[middle];
            if(target < temp){
                right = middle-1;
            }else if(target > temp){
                left = middle + 1;
            }else{
                // 这个条件判断很重要,如果不满足,则证明左边已经没有符合条件的元素了
                if(middle-1 >= left && nums[middle-1] == temp){
                    right = middle-1;
                    continue;
                }else{
                    res[0] = middle;
                    break;
                }
            }
        }
        // 找最后边出现的位置
            left = 0;
            right = nums.length-1;
            while(left <= right){
            int middle = (right -left) / 2 + left;
            int temp = nums[middle];
            if(target < temp){
                right = middle-1;
            }else if(target > temp){
                left = middle + 1;
            }else{
                left= middle+1;
                // 同理,如果不满足该条件,则证明后边已经没有符合条件的元素了
                if(right >= left && nums[left] == temp){
                    continue;
                }else{
                    res[1] = middle;
                    break;
                }
            }   
            }
            return res;
        }
    
}

总结

二分法很基础,保持住所谓的“循环不变量”很关键, 双指针法还得多练练,这个方法的应用场景太多了。
PS: 开工第一周,没适应过来,所以开始补进度hh。

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值