代码随想录算法训练营Day2——数组篇|977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵 II

本文讨论了LeetCode中的三个问题:有序数组平方利用双指针优化空间复杂度,长度最小子数组使用滑动窗口减少时间复杂度,以及螺旋矩阵II的数组模拟解法。通过实例分析了双指针和滑动窗口技巧的应用。
摘要由CSDN通过智能技术生成

Leetcode 977.有序数组的平方

题目链接:977. 有序数组的平方 - 力扣(LeetCode)

文档讲解:代码随想录 (programmercarl.com)

视频讲解:双指针法经典题目 | LeetCode:977.有序数组的平方_哔哩哔哩_bilibili

个人思考:

        看到这个题目,想到了可能会使用到双指针的操作,但一直想的是在原数组上进行操作,然后排序,这样的话在交换元素的时候就会很麻烦,没有想到重新申请一个新数组,然后双指针思想对数组进行排序。

暴力解法

思路:暴力解法的思路比较简单,就是遍历数组,得到平方后的数组,然后再对平方后的数组进行排序。

代码: 

class Solution {
    public int[] sortedSquares(int[] nums) {
       //遍历数组,得到平方后的数组
       for(int i = 0; i < nums.length; i ++){
            nums[i] =(int)Math.pow(nums[i], 2);
       }
       Arrays.sort(nums);//排序
       return nums;

    }
}

时间复杂度:O(n + nlogn)     空间复杂度:O(1) 

1.java.long.Math类里面的方法pow方法

 

2.Arrays.sort()方法对数组进行从小到大的排序。

双指针解法 

思路:原数组是有序的,因此平方之后也是有“有序”的特点在的,不过左边的负数平方后可能成为新数组中的最大值,因此新数组中元素的最大值要么出现在原数组的左边,要么出现在原数组的右边,不可能出现在中间。

图源:双指针思路演示

 

代码:

class Solution {
    public int[] sortedSquares(int[] nums) {
       //双指针解法
       int left = 0, right = nums.length - 1;
       int[] res = new int[nums.length];//创建一个新数组,保存最终结果
       int k = nums.length - 1;//从后往前依次存储
       while(left <= right){
            if(nums[left] * nums[left] < nums[right] * nums[right]){
                res[k --] = nums[right] * nums[right];
                right --;
            }else{
                res[k --] = nums[left] * nums[left];
                left ++;
            }
       }
       return res;
    }
}

 时间复杂度:O(n)    空间复杂度:O(n)

注意:循环条件是left <= right 不是left < right,否则会漏掉一个元素(新数组中最小的元素)

Leetcode 209.长度最小的子数组

题目链接:209. 长度最小的子数组 - 力扣(LeetCode)

文档讲解:代码随想录 (programmercarl.com)

视频讲解:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili

暴力解法

思路:这道题目暴力解法的思路就是找出这个数组的所有子序列,然后在所有子序列中找出长度最小的且值大于等于s的子数组序列。

代码:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int resLen = Integer.MAX_VALUE;//记录最终的结果
        int sum = 0;
        for(int i = 0; i < nums.length; i ++){//i记录子数组序列的起始位置
            for(int j = i; j < nums.length; j ++){//j记录子数组序列的终止位置
                    sum += nums[j];
                    if(sum >= target){
                        resLen = Math.min(resLen, j - i + 1);
                        break;//因为要找最小的,所以找到后break就行,不用累加后面的了
                    }
            }
            sum = 0;//记得处理一个子序列后,sum要初始化为0
        }
        return resLen == Integer.MAX_VALUE ? 0 : resLen;

    }
}

时间复杂度:O(n2)       空间复杂度:O(1) 

注意点:

1.一个子序列累加之后,开始尝试新的子序列的时候,记得sum初始化为0。(最开始写的时候,忘记了,结果一直在累加,导致程序出错。)

2.题目要求找长度最小的,因此找到之后直接break,不用累加后面的了。

3.暴力解法目前在leetcode上面提交已经超时了。

运行几个简单测试用例通过

提交显示超时

滑动窗口解法

       暴力解法中使用两个for循环来控制子数组序列的起始位置(外层for循环i)和终止位置(内层for循环j)。那么有没有什么方法能够使用一层for循环来实现呢?答案:滑动窗口

        只使用一层for循环,那应该用它来记录子数组序列的起始位置还是终止位置呢?

  • 起始位置?如果记录起始位置,那终止位置怎么记,仿佛又回到了暴力解法。
  • 终止位置?如果使用循环记录终止位置,那么起始位置怎么记,可以依赖target的值来动态确定。   

图源:代码随想录 (programmercarl.com) 

有个数组[2, 3, 1, 2, 4, 3],寻找target为7的子数组长度。

代码:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int resLen = Integer.MAX_VALUE;//记录最终的结果
        int i = 0, sum = 0;
        for(int j = 0; j < nums.length; j ++){//j用来记录子数组的终止位置
            sum += nums[j];
            while( sum >= target){
                resLen = Math.min(resLen, j - i + 1);//记录当前满足>=target条件的子数组长度
                sum -= nums[i];//缩小区间范围
                i ++;//窗口滑动
            }
        }
        return resLen == Integer.MAX_VALUE ? 0 : resLen;
    }
}

时间复杂度:O(n)     空间复杂度:O(1) 

注意:先缩小区间范围sum -= nums[i],再 i ++,两个顺序不要弄反了。

Leetcode 59.螺旋矩阵II

题目链接:59. 螺旋矩阵 II - 力扣(LeetCode)

文档讲解:代码随想录 (programmercarl.com)

视频讲解:一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili

思路:

       这道题目是数组模拟题目,主要就是再循环一圈又一圈的过程中,区间的左右边界上的处理,涉及数组区间问题,要考虑到底是使用左闭右开,还是左闭右闭,保持统一。

代码:

class Solution {
    public int[][] generateMatrix(int n) {
        //数组模拟题
        int[][] res = new int[n][n];
        int loop = 1;//圈的个数
        int num = 1;//记录填入矩阵的数
        int i , j ;
        int startx = 0, starty = 0;//存储每一圈开始的起始位置
        int offset = 1;//控制每一条边的遍历长度,每遍历一圈,右边界向内收缩一位
        while(loop <= n / 2){
            i = startx;
            j = starty;
            //从左到右(左闭右开)
            for(; j < n - offset; j ++ ){
                res[startx][j] = num ++;
            }

            //从上到下
            for(;i < n - offset ; i ++){
                res[i][j] = num ++;//此时j = n - 1;
            }

            //从右到左
            for(;j > starty; j -- ){
                res[i][j] = num ++;
            }

            //从下到上
            for(; i > startx; i--){
                res[i][j] = num ++;
            }
            startx ++;
            starty ++;
            offset ++;
            loop ++;
        }
        //最后处理最中心的元素(n为奇数的情况下)
            if( n % 2 != 0){
                res[n / 2][n / 2] = num ++;
        }
        return res;

    }
}

时间复杂度:O(n2)      空间复杂度:O(1)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值