有序数组的平方,长度最小子数组,螺旋矩阵解题思路

今天在leetcode上刷了三道题,分别是977.有序数组的平方,209.长度最小的数组,59.螺旋矩阵;977关键在于理解双指针思想,209在于使用滑动窗口的方法,同样也是基于双指针思想,而59并不考察算法,重点在于逻辑以及循环不变量原则。

那么话不多说,呈上做题思路及心得

977.有序数组的平方

题目描述:

给你一个按 非递减顺序 排序的整数数组 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]

提示:

  • 1 <= nums.length <= 104

  • -104 <= nums[i] <= 104

  • nums 已按 非递减顺序 排序

做题思路及心得:

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按非递减顺序排序。 遇到这样的问题我们可以使用暴力的解法,也就是把数组中的每一个数都平方一次后,重新排序,这个时间复杂度为O(nlogn),但我们学习了双指针法,那么我们就可以按照双指针法的思路来解决这个问题。 首先我们清楚数组本身是有序的,且为非递减顺序排列,而要求得到的新数组也是非递减排序的,那么我们就不能单纯的将元素平方,我们还需要考虑改变元素后它们的大小排列。首先一个有序数组每个元素平方后,最大的值一定是在两端,因为最小的元素可能是负数,但负数的平方可能会比原先最大正数的平方更大,那么我们就需要在改变元素后进行大小的比较。即从两头向中间。那么我们就可以考虑双指针法。 定义l指向起始位置,r指向终止位置,要得出一个新数组,那么我们就定义一个长度与原先数组长度相同的新数组res来存放新的元素。 那么只要l<r时,我们去比较两端的元素平方后大小,将大的赋值给新数组的终止位置元素,再将指针l或r进行相应的位移,循环比较下,最终即可得出有序的新数组。

代码如下:

class Solution {
    public int[] sortedSquares(int[] nums) {
       int l = 0;
       int r = nums.length - 1;
       int[] res = new int[nums.length];
       int j = nums.length - 1;
       while(l <= r){
           if(nums[l]*nums[l] > nums[r]*nums[r]){
               res[j--] = nums[l] * nums[l++];
           }else{
               res[j--] = nums[r] * nums[r--];
           }
       }
       return res;
    }
}

209长度最小的子数组

题目描述:

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]

输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]

输出:0

提示:

  • 1 <= target <= 109

  • 1 <= nums.length <= 105

  • 1 <= nums[i] <= 105

解题思路及心得:

当我们遇到: 给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。 这样的问题时,我们首先想到的就是嵌套两个for循环,不断寻找符合条件的子数组,但这样的时间复杂度就是O(n^2),显然不是最合适的方法。 那么我们就可以考虑另一种思路,也是双指针方法的变式方法,可以称之为滑动窗口。 那么为什么叫滑动窗口呢? 所谓滑动窗口,即不断地调节子序列的起始和终止位置,那么中间的区域我们就可以看作是一个窗口,这个窗口在不断地移动变换,从而得出我们想要的结果。 在暴力方法中,我们用了两个for循环,一个表示窗口的起始位置,一个表示终止位置,那么用滑动窗口时我们只用一个for循环来完成两个for循环的工作,此时一个问题摆在我们面前,这个循环的索引要表示初始位置还是终止位置呢?我们可以思考一下,如果表示的是初始位置,那么每当初识位置发生一次移,我们就需要再次遍历一遍剩下的终止位置,那么这和暴力解法中的两个for循环并没有什么本质的区别,那么这个索引一定表示的是终止位置。 那么我们要怎么去移动呢,那么我们首先需要初始化一个起始位置l,一个数值的和sum以及一个用来记录子数组长度的result,因为我们需要和target比较大小,那么在一个for循环中,我们首先不断移动终止位置的索引j,sum则不但累加这些元素,直到sum >= target时,此时我们需要记录当前子数组的长度并将它赋给result,那么接下来就应该去移动初始位置i,直到sum不满足大于等于target时,初始位置固定,再去移动终止位置索引j,直到遍历完成,得到最小个数的子数组元素个数。 代码如下:

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        //滑动窗口
        int left = 0;
        int sum = 0;
        int result = Integer.MAX_VALUE;
        for(int right = 0;right < nums.length; right++){
            sum += nums[right];
            while(sum >= target){
                result = Math.min(result,right - left +1);
                sum -= nums[left++];
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

那么为什么用while而不用if呢,我们要知道这是一个循环遍历,当if被满足一次后,就会跳出此次判断,而无法进行后续的遍历,所以我们要用while来不断循环遍历,直到全部遍历结束。 那么就会有一些朋友疑问,这也是两个循环为什么时间复杂度是O(n),那么并不是for里放一个while就是O(n^2),而是要看每一个元素被操作的次数,每个元素在滑动窗口进来时操作一次,出去时操作一次,每个元素都被操作两次,那么时间复杂度就是2*n,即O(n).

59螺旋矩阵

题目描述:

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

示例 1:

输入:n = 3

输出:[[1,2,3],[8,9,4],[7,6,5]]

示例 2:

输入:n = 1

输出:[[1]]

提示:

  • 1 <= n <= 20

做题思路及心得:

第一次接触了螺旋矩阵的问题,这道题并不涉及算法,就是要模拟过程,但很容易让人在编码过程当中混乱。 本题最重要的就是需要坚持循环不变量的原则。 我们在模拟顺时针画矩阵的过程时,要保证每一条边遍历的原则是相同的,每一条边都坚持左闭右开或者左开右闭,这样一圈才能按照统一的规则画下来。 那么当我们坚持统一原则循环时,就会避免很多问题,只需要细心根据规则编码就好。 在编码过程我们要注意,我们需要初始化起始的位置,初始化一个容量为n^2的二维数组,并且定义一个控制循环次数的变量,填充数字是从1到n^2,那么我们还需要定义一个填充数字count,那么要绕几圈也是一个问题,如果给定n,那么就需要绕n/2圈,n是偶数的话可以全部绕完,但如果n为奇数,则会剩下最中间的一个空间,需要我们手动填充。 代码如下:

class Solution {
    public int[][] generateMatrix(int n) {
        int loop = 0;//控制循环次数
        int[][] res = new int[n][n];
        int start = 0;//每次循环的开始点(start,start)
        int count = 1;//定义填充数字
        int i , j;
        while(loop++ < n/2){//判断边界后,loop从1开始
            //模拟上侧从左到右
            for( j = start;j < n - loop;j++){
                res[start][j] = count++;
            }
            //模拟右侧从上到下
            for( i = start;i < n - loop;i++){
                res[i][j] = count++;
            }
            //模拟下侧从左到右
            for(;j > start;j--){
                res[i][j] = count++;
            }
            //模拟左侧从下到上
            for(;i > start;i--){
                res[i][j] = count++;
            }
            start++;
        }
        if(n % 2 == 1){
            res[start][start] = count;
        }
        return res;
    }
}

总结: 要坚持循环不变量原则,在绕圈时,要保证规则的统一,同时要注意一些变量值应该怎么变化,细心思考。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值