Bootstrap

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

977.有序数组的平方

使用双指针法解题。
为什么能想到用双指针呢?因为有序数组的平方不一定是有序数组,但是最大值一定是从两边向中间递减。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size() - 1;
        vector<int> result(n + 1);
        for (int i = 0, j = n; i <= j;) {
            if (nums[i] * nums[i] <= nums[j] * nums[j]) {
                result[n--] = nums[j] * nums[j];
                --j;
            }
            else {
                result[n--] = nums[i] * nums[i];
                ++i;
            }
        }
        return result;
    }
};

209.长度最小的子数组

暴力解法

两个for循环,第一个for循环遍历子数组起始位置,第二个for循环遍历对应的终止位置。时间复杂度是 O ( n 2 ) O(n^2) O(n2).

滑动窗口

为什么会考虑到用滑动窗口呢?什么是滑动窗口呢?怎么就能解决问题呢?

  1. 为什么用滑动窗口:暴力算法是两个for循环,考虑能否在一个for循环里面解决问题,和双指针一样。
  2. 什么是滑动窗口:就是两个指针之间的集合

怎么解决:

  1. for循环遍历的是起始指针还是终止指针?如果是起始指针,思路和暴力解法一样。所以必须是终止指针。
  2. 起始指针怎么移动?起始和终止指针初始位置都在0,其中元素只包含第一个。当滑动窗口中元素和大于s时,起始指针向结尾移动,缩小窗口。
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int result = nums.size() + 1;
        #if 0
        // brute force
        for (int i = 0; i < nums.size(); i++) {
            int tmp = 0;
            int count = 0;
            for (int j = i; j < nums.size(); j++) {
                tmp += nums[j];
                count ++;
                if (tmp >= target) {
                    result = (count < result) ? count : result;
                    break;
                }
            }
        }
        #endif
        // 滑动窗口
        int left = 0, right = 0;
        int sum = 0;
        int count = 0;
        for (; right < nums.size(); right++) {
            sum += nums[right];
            while (sum >= target) {
                count = right - left + 1;
                result = (count < result) ? count : result;
                sum -= nums[left++];
            }
        }
        return (result == nums.size() + 1) ? 0 : result;
    }
};

59.螺旋矩阵II

每一条边都要注意区间的定义。这样会让边界条件更容易处理。

所以真正解决题目的代码是简洁的,有原则性的。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> result(n, vector<int>(n, 0));
        int loop = n / 2;
        int startx = 0, starty = 0;
        int offset = 1;
        int count = 1;
        while (loop--) {
            int x = startx;
            int y = starty;
            for (; y < n - offset; y++)
                result[x][y] = count++;
            for (; x < n - offset; x++)
                result[x][y] = count++;
            for (; y > startx; y--)
                result[x][y] = count++;
            for (; x > starty; x--)
                result[x][y] = count++;
            startx++;
            starty++;
            offset++;
        }
        if (n % 2)
            result[n/2][n/2] = count;
        return result;
    }
};

数组总结

  • 数组在内存中地址是连续的,是不能直接删除的,只能覆盖。因此容器vector的erase的时间复杂度是 O ( n ) O(n) O(n).
  • 从二分查找的例子中,我们要记住搜索区间定义的重要性。
  • 从移除元素中我们要掌握双指针的概念,要能够说出每一个指针所代表的实际含义,而不是背答案。
  • 从长度最小的子数组中,我们也要理解滑动窗口的含义。
  • 最后,螺旋矩阵再一次体现了搜索区间的精准定义,会减少很多边界条件的麻烦。
;