代码随想录算法训练营第一天 | 704. 二分查找,27. 移除元素,977.有序数组的平方

Day 01 总结

  • 自己实现过程中遇到哪些困难
    • 特殊例子,或者特殊情况没有考虑完全,明明是一道简单题,但不能答对。
    • 写第三题的时候,发现自己对于一些特殊情况并不能很好的照顾到,设计的算法表面上看起来似乎是对的,没有问题,但是跑起来测试之后会有很多漏洞。
    • 我应该在设计完算法之后试着去寻找一些反例,或者思考一下测试数据的多种可能性,往往我设计的算法都只能通过测试用例,其中潜在原因就是思考的并不全面,也没想到漏洞在哪里。
    • 请问对于这种情况,我应该如何去解决呢?
  • 今日收获,记录一下自己的学习时长
    • ​​​​​​​14:00 - 16:00​​​​​​​​​
  • ​​​​​​​​​​​​​​解惑(补充)
    • 对于第三题来说,看了卡哥的讲解视频,我一下就发现了自己的错误在哪里。数组的最大值是一定可以在两端找到的,但是对于最小值,并不能确定其索引的位置,有可能出现两个最小值,也有可能数组的形状并不是V字形(单调递减),所以寻找最小值向左右扩散是会遇到特殊情况的。
    • 所以我的算法会遇到不能处理的情况,是因为我没有总结出题目的规律性和潜在的特殊性。
    • 需要充分了解到问题,才能设计解决问题的通用办法, 不要陷入在debug的陷阱中,而是去思考全局的方向是否出现错误。
    • 也就是说,并不是有特殊情况的存在,使得我的算法失效,而是我没有深入理解问题的本质。

704. 二分查找(简单)

力扣题目链接:

实现思路:

由变量 left 和 变量 right 确定一个数组区间,通过公式 mid = left + (right - left) / 2 计算出中间位置的索引。

对比对索引位置的值与 target 值,因为数组有序,如果查找到的值比目标值小,则继续搜索左区间,反之搜索右区间。对于左区间来说,所有的数必然小于当前搜索位置,所以 left 值不需要变动,只需要更新 right 值。搜索右区间同理。

确定数组区间边界:

需要明确 left 和 right 的意义

如果 left 的值代表区间最左边的值的索引,right 代表区间最右边的值的索引,那么该区间是一个左边闭合,右边也闭合,即,左闭右闭的区间,那么在更新 left 值的时候,left = mid + 1,更新 right 值的时候,right = mid - 1。循环终止条件,即当前区间长度 >= 1的表达式为 left >= right。如果为左闭右边开区间,left 的更新方式不变,right = mid。循环终止条件,当前区间长度 >= 1的表达式为 left > right, 因为 right 的位置不属于区间内,left 不会与 right 重合。

Java代码实现:

class Solution {
    /*
     * 左闭右闭
     */
    public int search1(int[] nums, int target) {
        int right = nums.length-1;
        int left = 0; 
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {return mid;}    
            if (nums[mid] < target) {left = mid + 1;} // 搜索右区间
            else right = mid-1;                       // 搜索左区间
        }
        return -1;
    }

    /*
     * 左闭右开
     */
    public int search2(int[] nums, int target) {
        int right = nums.length;
        int left = 0; 
        while (left < right) {
            int mid = left + (right - left) / 2;
            System.out.println(mid);
            if (nums[mid] == target) {return mid;}
            if (nums[mid] < target) {left = mid + 1;}
            else right = mid;
        }
        return -1;
    }
}

27. 移除元素(简单)

力扣题目链接:

. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。icon-default.png?t=O83Ahttps://leetcode.cn/problems/remove-element/

文章讲解:

代码随想录代码随想录PDF,代码随想录网站,代码随想录百度网盘,代码随想录知识星球,代码随想录八股文PDF,代码随想录刷题路线,代码随想录知识星球八股文icon-default.png?t=O83Ahttps://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html

实现思路:

在同一个数组中,将符合条件的元素向后移动,并不改变其他元素的相对顺序。在同一数组中首先想到使用双指针,slow 指针定位需要向后移动的元素,fast指针定位需要向前移动的元素,然后在一次循环当中进行交换,并更新两个指针的位置。

代码实现:

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

977. 有序数组的平方

力扣题目链接:

文章讲解:

实现思路:

我一开始的思路

对数组排序还要求 O(n) 的复杂度必然需要使用额外空间,所以需要创建一个新的数组。

由于负数的存在,在平方后,整个数组先是从高到底下降的趋势,然后在从某一个位置开始逐渐增高,呈现一个V字山谷的形状。在了解到这个变化趋势之后,可以先定位到谷底的位置,将该数组插入到新数组的第一个位置,然后从谷底开始左右扩散,拿出更小的的值依次放入数组中,实现排序。

但是实测发现,会出现很多bug,对于很多情况都不能处理,例如

[25,9,4,1]

[100000000,0,0,25,49,99980001,100000000]

错误出现的原因:谷底并不唯一

文章的双指针思路

从数组的最左侧和最右侧开始,选择更大的数字放入新数组的最右侧,省去了寻找谷底的步骤,更加简洁易懂和好实现。

代码实现:

class Solution {
   public int[] sortedSquares(int[] nums) {
        int n = nums.length;;
        for (int i=0; i<nums.length; i++) {
            nums[i] *= nums[i];
        }

        int left =0,
            right = nums.length-1;
        int[] result = new int[n];
        for (int i=n-1; i>=0; i--) {
            int temp;
            if (nums[left] >= nums[right]) temp = nums[left++];
            else temp = nums[right--];
            result[i] = temp;
        }

        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值