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

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

977.有序数组的平方

题目链接:977.有序数组的平方
注:题目所给条件为 1.非递减顺序排序的数组;2.时间复杂度为 O(n)。

暴力解法

思路:遍历数组,对每个元素进行平方后,快速排序。该方法时间复杂度为O(nlogn),即快速排序的时间复杂度。

class Solution {
    public int[] sortedSquares(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = nums[i] * nums[i];
        }
        Arrays.sort(nums); //快速排序
        return nums;
    }
}

双指针法

思路:由于原数组已按非递减顺序排列,则最大值只可能出现在原数组两端。因此分别从数组首尾开始对比(双指针),将其中平方后的较大值从后往前添加至新数组,对应指针向原数组中间靠拢。最后返回新数组即可。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int[] result = new int[nums.length]; //最后返回的新数组
        int k = nums.length - 1; //从后往前向新数组添加元素
        for (int i = 0, j = nums.length - 1; i <= j;){ //若i < j,则会遗漏最中间的元素
            if (nums[i] * nums[i] > nums[j] * nums[j]){
                result[k] = nums[i] * nums[i];
                k--;
                //以上两句可合并为 result[k--] = nums[i] * nums[i];
                i++;
            } else {
                result[k] = nums[j] * nums[j];
                k--;
                j--;
            }
        }
        return result;
    }
}

209.长度最小的子数组

题目链接:209.长度最小的子数组
注:题目要求空间复杂度为O(1)。

暴力解法

思路:两个for循环。 不断寻找符合条件的子数组,该方法的时间复杂度为O(n2),空间复杂度为O(1)。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int result = Integer.MAX_VALUE; //最终返回的长度
        int sum = 0;    //子数组的元素之和
        int subLength = 0;  //子数组的长度
        for(int i = 0; i < nums.length; i++){
            sum = 0;
            for(int j = i; j < nums.length; j++){
                sum += nums[j];
                if(sum >= target){
                    subLength = j - i + 1;
                    result = result < subLength ? result : subLength;
                    break;
                }
            }
        }
        //若result为初始值,则说明没有符合条件的子数组
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

双指针法(滑动窗口)

思路:类似一个向前滑动的窗口,不断调节子数组的起始位置和终止位置。
使用该方法要点有三:

  • ①窗口内元素是什么?
  • ②如何移动窗口的起始位置?
  • ③如何移动窗口的终止位置?
    针对本题,窗口内元素即为子数组的和;若当前窗口内值的和大于target,则起始位置向后移动;终止位置即for循环遍历数组的指针。精髓在于根据当前子数组和的大小,不断变更子数组的起始位置。该方法的时间复杂度为O(n),空间复杂度为O(1)。
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int i = 0; //滑动窗口起始位置
        int sum = 0; //滑动窗口的数值之和
        int subLength = 0; //滑动窗口的长度(子数组长度)
        int result = Integer.MAX_VALUE; //最后返回的结果,取最大值旨在便于最后的返回
        for (int j = 0; j < nums.length; j++){
            sum += nums[j];
            //while循环不断比较子数组是否符合条件,并更新起始位置(i)
            //注意不能使用if语句,例如[1,1,1,1,1,100],target也为100的情况
            while (sum >= target){
                subLength = (j - i + 1); //获取子数组长度
                result = result < subLength ? result : subLength;
                sum -= nums[i];
                i++;
                // 以上两句等于 sum -= nums[i++]; 此处即为关键部分,不断变更起始位置(i)
            }
        }
        //若result为初始的最大值,则说明没有符合条件的子数组,因此返回0
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

59.螺旋矩阵II

题目链接:59.螺旋矩阵II
注:本题并不涉及算法,主要考察对代码的掌控能力。

解法

思路:while循环模拟由外向内一圈圈打印数组。 坚持循环不变量原则:每条边均以左闭右开的方式进行遍历。

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] result = new int[n][n];
        int startX = 0; //起始行
        int startY = 0; //起始列
        int count = 1; //表示每个待填入的数值
        int offset = 1; //用以控制每圈变化的边长
        int i = 0;
        int j = 0;
        int loop = n / 2; //共转几圈
        int middle = n / 2; //矩阵中间位置
        while (loop-- != 0){
            // [i, j]
            //四个for模拟顺时针转圈打印四条边的过程
            for (j = startY; j < n - offset; j++) { //j向右移动
                result[startX][j] = count++;
            }
            for (i = startX; i < n - offset; i++) { //i向下移动
                result[i][j] = count++;
            }
            for (; j > startY; j--) { //j向左移动
                result[i][j] = count++;
            }
            for (; i > startX; i--) { //i向上移动
                result[i][j] = count++;
            }
            startX++; //修改下一圈的初始行
            startY++; //修改下一圈的初始列
            offset += 1; //层数加深,每条边长减小
        }
        if (n % 2 == 1) {
            result[middle][middle] = n * n;
        }
        return result;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值