算法学习第二天| 力扣977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

算法第二天-加深双指针的理解和学习滑动窗口的使用


今天主要是加强了对双指针的理解和学习了滑动窗口的使用,对于双指针,感觉掌握良好。而对滑动窗口来说,虽然是理解了它的过程,但是自己实现代码的时候还是有点困难,在看代码解析的时候才较为清晰。螺旋矩阵II我之前实现过一次,自己做出来的,其实这题没有什么技巧,只要确定好边界问题,模拟过程出来就好了。

相关题目:

977.有序数组的平方:
https://leetcode.cn/problems/squares-of-a-sorted-array/

209.长度最小的子数组:
https://leetcode.cn/problems/minimum-size-subarray-sum/

59.螺旋矩阵II:
https://leetcode.cn/problems/spiral-matrix-ii/

关于 977.有序数组的平方

题目描述
https://leetcode.cn/problems/squares-of-a-sorted-array/

● 给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序排序。
● 示例 1: 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
● 示例 2: 输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]

看到该题目的第一想法,就是暴力破解,当然,暴力破解是大多数题都可以的解法,但往往也不是最好的解法。除了暴力解法,我还根据今天的内容去用双指针进行解题。
题目给出的数组是整数的,而且非递减顺序,也就是说有负数,0和正数,而且从小到大排序好的。我的第一想法就是头尾相向双指针来操作,实际的解法也是如此,但我陷入了一个牛角尖,自认为不能申请其他空间进行操作了。就想着怎么原地操作,结果搞了半小时还是没弄出来。当看到解析是新建了一个数组的时候,心都要凉凉了。

该题的思路:
1.暴力破解,通过先给每个数平方,然后再进行排序。
2.双指针法:数组整体是有序的,只是在数组中可能有负数,故平方后负数的平方可能比正数的平方大,如果给原数组价格绝对值,那么最大值肯定在两端,不是最左边就是最右边,不可能是中间。这样的话,只要我们申请一个和原数组相同大小的新数组,让头尾双指针分别指向原数组头尾,通过比较原数组头尾的元素平方值,将较大的值放到新数组的最后即可。
在这里插入图片描述
977.有序数组的平方的双指针法代码实现

//977.有序数组的平方
//双指针实现只需要循环一遍数组,故时间复杂度为O(n)
public static int[] sortedSquares2(int[] nums) {
    int left = 0, right = nums.length - 1;
    //新数组的最后一个元素坐标
    int k = nums.length - 1;

    //新的排序后的数组
    int[] newNums = new int[nums.length];

    //平方后最大的值不是在头就是在尾 故头尾比较,较大的插入新数组的末尾
    while (left <= right) {
        if (nums[left] * nums[left] > nums[right] * nums[right]) {
            newNums[k--] = nums[left] * nums[left];
            left++;
        } else {
            newNums[k--] = nums[right] * nums[right];
            right--;
        }
    }
    return newNums;
}

关于 209.长度最小的子数组

题目描述 https://leetcode.cn/problems/minimum-size-subarray-sum/
● 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
● 示例:输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

知道这个题目是用滑动窗口的方式进行解题的,而且之前也做过用户滑动窗口的题,基本思路还在,模模糊糊的也忘了一点。我就差个把每次超过目标值时所需要的元素个数存起来。

该题的思路:
1.暴力破解:无非就是通过两层循环,先从第一个元素开始,往后累加,记录达到目标值后的元素个数,然后结束,第二趟循环从第二个元素开始,以此类推,得到最小的元素个数即可。
2.滑动窗口:所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。它的过程很像一条毛毛虫向前蠕动。首先要给定一个变量存储最少元素的个数,通过不断比较,最后的变量值就是要返回的数据。循环是一遍,故循环条件就是right到达数组尾巴。while (right < nums.length),在循环里,先求得当前right所在下标前的元素总和,然后比较目标值,若sum大于等于目标值,就记录该次的长度,同时进行收缩,sum减去left所在下标的元素值,最后通过比较result值即可。
在这里插入图片描述 在这里插入图片描述

滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

209.长度最小的子数组滑动窗口解法

//时间复杂度:O(n)
//不要以为for里放一个while就以为是O(n^2),主要是看每一个元素被操作的次数,
//每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,
//所以时间复杂度是 2 × n 也就是O(n)。
//空间复杂度:O(1)
public static int minSubArrayLen(int target, int[] nums) {
    int sum = 0, left = 0, right = 0;
    int result = Integer.MAX_VALUE; //存放最少个数
    while (right < nums.length) {
        sum += nums[right];
        while (sum >= target) { //进行比较
            result = Math.min(result, right - left + 1); //若大于目标值,则计算元素个数
            sum -= nums[left++];  //同时进行收缩,减去最开始的一个值
        }
        right++;   //若不大于目标值,则继续添加
    }
    return result == Integer.MAX_VALUE ? 0 : result;
}

关于 59.螺旋矩阵II

题目描述
https://leetcode.cn/problems/spiral-matrix-ii/

● 给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
● 示例:输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]

一看到这个题目的时候,心里窃喜,因为我之前做过这题,而且还是完全自己做出来的,这题没什么技术要求,就是感觉模拟过程,一趟一趟的循环,比较难的就是循环次数和边界的确定,最后还有注意数组个数的奇偶,若是奇数个元素,则还需单独给他赋值。

该题的思路:
要坚持好最初的循环原则!一趟一趟进行循环,从而模拟出画矩阵的过程。
模拟顺时针画矩阵的过程:由外向内一圈一圈这么画下去。
● 填充上行从左到右
● 填充右列从上到下
● 填充下行从右到左
● 填充左列从下到上
若我按照当前行货列最后一个元素不取,而留给下一个循环当做起始元素的原则的话,则按照下图,第一次我会填充4趟4个元素,然后再4趟4-2=2个元素的循环,因为一趟循环下来,左右两边会减少两个元素。所以最重要的就是循环的次数和每次循环的步长!
在这里插入图片描述
螺旋矩阵II实现代码

public static int[][] generateMatrix2(int n) {
    int[][] ints = new int[n][n];
    int x = 0, y = 0;
    //走的步长
    int step = n - 1;
    //i记录该放入的数字,范围为1~n*n
    int i = 1;

    //没有等号的原因是因为在下面赋值的时候会i++
    while (i < n * n) {
        //从左往右 j代表步长
        for (int j = 0; j < step; j++) {
            ints[x][y++] = i++;
        }
        //从上往下
        for (int j = 0; j < step; j++) {
            ints[x++][y] = i++;
        }
        //从右往左
        for (int j = 0; j < step; j++) {
            ints[x][y--] = i++;
        }
        //从下往上
        for (int j = 0; j < step; j++) {
            ints[x--][y] = i++;
        }
        x++;
        y++;
        //因为最左侧和最右侧都有值了,故-2
        step -= 2;
    }
    //判断最后一个是否需要再次添加,若n为偶数则不用
    if (n % 2 == 1) {
        ints[x][y] = n * n;
    }
    return ints;
}

完成了简单入门,有时间会加强对相关知识点的题型训练,第二天结束,第三天加油!

参考文章:
https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html
https://programmercarl.com/%E6%95%B0%E7%BB%84%E6%80%BB%E7%BB%93%E7%AF%87.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值