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

1. 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 已按 非递减顺序 排序

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题

 1.1 暴力

平方后再排序。

 1.2 双指针

如何想到双指针的写法:因为最大的数一定在两个指针之一

class Solution {
    public int[] sortedSquares(int[] nums) {
        int[] ans = new int[nums.length];
        int left = 0, right = nums.length - 1;
        int a = nums.length - 1;
        while (left <= right) {
            if (nums[left] * nums[left] >= nums[right] * nums[right]) {
                ans[a--] = nums[left] * nums[left];
                left++;
            } else {
                ans[a--] = nums[right] * nums[right];
                right--;
            }
        }
        return ans;

    }
}

2. 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
 

进阶:

如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

2.1 暴力 

是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。

2.2 滑动窗口 

思路

  • 循环索引下标应为终止位置。若为起始位置,最差的情况下所有起始位置和所有终止位置都要遍历,则跟暴力解法一样了。
  • 滑动窗口最重要的是如何移动起始位置。
  • 在本题中实现滑动窗口,主要确定如下三点:

  1. 窗口内是什么?
  2. 如何移动窗口的起始位置?
  3. 如何移动窗口的结束位置?

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于等于s了(也就是wihle条件是sum与s的关系),窗口就要向前移动了(也就是该缩小了)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

解题的关键在于 窗口的起始位置如何移动,如图所示:

leetcode_209

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

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

    }
}

2.3 相关题目

        904. 水果成篮 

        76. 最小覆盖子串

3. 59. 螺旋矩阵 II 

 题目

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

示例 1:

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

示例 2:

输入:n = 1
输出:[[1]]

提示:

  • 1 <= n <= 20

思路:

1、每圈处理:有几圈

2、每条边处理:如何定义区间(左闭右开、还是左闭右闭),左闭右开每条边处理逻辑才是类似的。

3.1 分层解法

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] ans = new int[n][n];
        // 00,01,02,12,22,21,20,10,11
        // 1、每圈作为一个循环:知道几个圈,每个圈边长,起始点:00、11、22...
        // 2、每条边左闭右开:每条边处理第一个节点,不处理最后一个节点
        int count = n / 2 + n % 2;
        System.out.println(count);
        int cur = 1;
        for (int i = 0; i < count; i++) {
            // 边长
            int len = n - i * 2;
            // 起始点
            int j = i, k = i;
            // System.out.println(count + " " +cur + " " +len + " "+ j + " " +k );
            // 只有一个节点,一个节点即为最后一个节点,特殊处理,且循环结束
            if (k == i + len - 1) {
                ans[j][k] = cur;
                break;
            }
            // 第一条边,都不处理最后一个节点
            while (k < i + len - 1) {
                ans[j][k++] = cur++;
            }
            while (j < i + len - 1) {
                ans[j++][k] = cur++;
            }
            while (k > i) {
                ans[j][k--] = cur++;
            }
            while (j > i) {
                ans[j--][k] = cur++;
            }
        }
        return ans;

    }
}

3.2 类似题目

        54. 螺旋矩阵

        LCR 146. 螺旋遍历二维数组

4 总结 

数组的经典方法:

  • 二分法:例如力扣上的704.二分查找,通过从中间元素开始往左右查找,避免一次遍历所有元素。二分查找法的时间复杂度为O(logn)。
  • 双指针法:通过一个快指针和一个慢指针在一个for循环下,完成两个for循环的工作。也存在使用左右指针的方式。快慢指针从同一个地点遍历方向相同,左右指针从一头一尾往中间进行遍历。
  • 滑动窗口:通过两个指针建立一个滑动窗口,滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。
  • 模拟行为:同样在数组内很常见,要确定好模拟的需求,确定循环体,保持循环不变量原则。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值