2023-09-07 Day 2 有序数组的平方、长度最小的子数组和螺旋矩阵II

本日刷题继续练习数组相关的题目。进一步熟悉双指针的使用方法。

977 有序数组的平方 Squares of a Sorted Array

题目
Given an integer array nums sorted in non-decreasing order, return an array of the squares of each number sorted in non-decreasing order.

Example 1:
Input: nums = [-4,-1,0,3,10]
Output: [0,1,9,16,100]
Explanation: After squaring, the array becomes [16,1,0,9,100].
After sorting, it becomes [0,1,9,16,100].

Example 2:
Input: nums = [-7,-3,2,3,11]
Output: [4,9,9,49,121]

Constraints:

  • 1 <= nums.length <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • nums is sorted in non-decreasing order.

要点

  1. 可以直接顺着题目的叙述,先遍历数组,再排序(处理负数平方后大的问题)。遍历的复杂度是 O(N),快速排序 sort(nums.begin(), nums.end()) 的复杂度是 O(NlogN)。重点注意记住 C++ Python 语言的默认排序函数用法
  2. 题目在最后让我们思考 O(N) 的解法,可以使用双指针(相向处理,因为平方最大的肯定在两端,遍历一次即可把平方后的值按非递减顺序排好。

代码

暴力遍历再排序

时间复杂度 O(NlogN) (N + NlogN)

C++

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        // O(NlogN)
        int size = nums.size();
        vector<int> nums_sqrs(size);
        for (int i = 0; i < size; i++) {
            nums_sqrs[i] = nums[i] * nums[i];
        }
        // sort
        sort(nums_sqrs.begin(), nums_sqrs.end());
        return nums_sqrs;
    }
};

Python 注意 list.sort() 方法是原位操作。

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        nums_sqrs = []
        for i in range(len(nums)):
            nums_sqrs.append(nums[i] ** 2)
        nums_sqrs.sort()
        return nums_sqrs

双指针

时间复杂度 O(N)

C++

两次写了两版代码,略有区别,第一次是把 kfor 遍历了,内层嵌套 while (i<=j)。由于其实
k 遍历完整与 while (i<=j) 是同一时刻,所以应该没什么太大问题。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int i = 0;
        int j = nums.size() - 1;
        vector<int> sorted_sqrs(nums.size());

        for (int k = nums.size() - 1; k >= 0; k--) {
            while (i <= j) {
                if (nums[i] * nums[i] > nums[j] * nums[j]) {
                    sorted_sqrs[k--] = nums[i] * nums[i++];
                } else {
                    sorted_sqrs[k--] = nums[j] * nums[j--];
                }
            }
        }
        return sorted_sqrs;
        }
};
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int left = 0;
        int right = nums.size() - 1;
        vector<int> nums_sqrs(nums.size());
        int idx = nums.size() - 1;

        while (left <= right) {
            if (nums[left] * nums[left] < nums[right] * nums[right]) {
                nums_sqrs[idx--] = nums[right] * nums[right--];
            } else {
                nums_sqrs[idx--] = nums[left] * nums[left++];
            }
        }
        return nums_sqrs;
    }
};

Python

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        left = 0
        right = len(nums) - 1
        sorted_sqrs = [0 for _ in range(len(nums))]
        idx = len(nums) - 1

        while left <= right:
            if nums[left] ** 2 < nums[right] ** 2:
                sorted_sqrs[idx] = nums[right] ** 2
                right -= 1
                idx -=1
            else:
                sorted_sqrs[idx] = nums[left] ** 2
                left += 1
                idx -= 1
        return sorted_sqrs

209 长度最小的子数组 Minimum Size Subarray Sum

Given an array of positive integers nums and a positive integer target, return the minimal length of a
subarray
whose sum is greater than or equal to
target. If there is no such subarray, return 0 instead.

Example 1:

Input: target = 7, nums = [2,3,1,2,4,3]
Output: 2
Explanation: The subarray [4,3] has the minimal length under the problem constraint.

Example 2:

Input: target = 4, nums = [1,4,4]
Output: 1

Example 3:

Input: target = 11, nums = [1,1,1,1,1,1,1,1]
Output: 0

Constraints:

1 <= target <= 10^9
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^4

Follow up: If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log(n)).

要点

  1. 双指针实现比较直观。右侧指针每往右侧移动一步,当前的和 curr_sum 加上右侧移动后位置的值。每次挪动左指针都要判断 curr_sum 是否大于等于(greater or equal to) target,所以用 while (curr_sum >= target) 而非 if (只判断一次后左指针动一次)。
  2. 此题主要记忆求最小值的时候,初始化可以初始化成最大的整型:C++ INT_MAX ,Python 里可以写成 import mathmath.inf 或者 float("inf")

代码

双指针

时间复杂度 O(N)

C++

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int i = 0;
        int min_length = INT32_MAX;
        int curr_sum = 0;

        for (int j = 0; j < nums.size(); j++) {
            curr_sum += nums[j];
            while (curr_sum >= target) {
                min_length = min(min_length, (j - i + 1));
                curr_sum -= nums[i++];
            }
        }
        if (min_length == INT32_MAX) {
            return 0; // no such subarray
        }
        return min_length;
    }
};

Python

import math

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        i = 0
        min_length = math.inf
        curr_sum = 0

        for j in range(len(nums)):
            curr_sum += nums[j]
            while curr_sum >= target:
                min_length = min(min_length, (j - i + 1))
                curr_sum -= nums[i]
                i += 1
        
        if min_length == math.inf:
            return 0
        return min_length

59 螺旋矩阵II Spiral Matrix II

Given a positive integer n, generate an n x n matrix filled with elements from 1 to n^2 in spiral order.

Example 1:

Input: n = 3
Output: [[1,2,3],[8,9,4],[7,6,5]]

Example 2:

Input: n = 1
Output: [[1]]

Constraints:

  • 1 <= n <= 20

要点

  1. 关注循环不变量,思路上轻松的方法就是保证左开右闭。按照代码随想录的题解思路,遍历上、右、下、左的时候,都左开右闭。转一圈后距离边界的 offset + 1,总 offset 数为 n // 2。当 n 为奇数的时候,还需要补充中间的值。这个写法其实从流程上总体有点麻烦的。
  2. 把过程写得更简略一些,则是每次都把这一行一列遍历完,立刻挪动遍历完的这一行或者列的边界值。也不需要补充中间值了。
  3. 注意记忆初始化矩阵的方式:
    a. C++ vector<int> vec(n); vector<vector<int>> mat(n, vec);
    b. Python [[0 for _ in range(n)] for _ in range(n)]
  4. 注意行和列的索引!先索引的是行,然后是列。

代码

模拟每次挪动的写法

C++

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<int> vec(n);
        vector<vector<int>> mat(n, vec);
        int t = 0, l = 0, b = n - 1, r = n - 1;
        int k = 1;

        while (k <= n * n) {
            // left to right, top++
            for (int i = l; i <= r; i++) {
                mat[t][i] = k++;
            }
            t++;
            // top to bottom, right--
            for (int i = t; i <= b; i++) {
                mat[i][r] = k++;
            }
            r--;
            // right to left, bottom--
            for (int i = r; i >= l; i--) {
                mat[b][i] = k++;
            }
            b--;
            // bottom to top, left++
            for (int i = b; i >= t; i--) {
                mat[i][l] = k++;
            }
            l++;
        }
        return mat;
    }
};

Python

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        mat = [[0 for _ in range(n)] for _ in range(n)]
        l, t, b, r = 0, 0, n - 1, n - 1
        k = 1

        while k <= n * n:
            # left to right, top++
            for i in range(l, r + 1):
                mat[t][i] = k
                k += 1
            t += 1
            # top to bottom, right--
            for i in range(t, b + 1):
                mat[i][r] = k
                k += 1
            r -= 1
            # right to left, bottom--
            for i in range(r, l - 1, -1):
                mat[b][i] = k
                k += 1
            b -= 1
            # bottom to top, left++
            for i in range(b, t - 1, -1):
                mat[i][l] = k
                k += 1
            l += 1
        
        return mat 

数组总结

  • 数组的内存空间地址连续,所以删除或者增添元素的时候就需要移动其它元素的地址。
  • 数组的元素不能删除,只能覆盖

数组常见题目类型

  1. 二分法 注意循环不变量的问题。多写多练习,很容易错
  2. 双指针法 一个快指针和一个慢指针,在一层循环里完成两层循环的工作
  3. 滑动窗口 根据当前子序列大小和状况,不断调节子序列起始位置,将 O(n^2) 降低为 O(n)
  4. 模拟行为 注意循环不变量,辅以画图、写好注释以防搞乱。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容