day 2 |977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II

目录:

解题及思路学习

977.有序数组的平方

https://leetcode.cn/problems/squares-of-a-sorted-array/submissions/

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]

思考:双指针法

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

时间复杂度为O(n)

空间复杂度为O(1)

209.长度最小的子数组

https://leetcode.cn/problems/minimum-size-subarray-sum/

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

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

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2

思考:需要找到满足和≥ target的最短子数组长度。利用双指针法,统计两个指针之间的和。当两个指针之间的数值count < target的时候,指针就一直往前移动。当 ≥ target的时候,记录下最短的距离。然后左边的指针就往右移,看是否满足。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int result = INT32_MAX;
        int i = 0; 
        int sum = 0;
        int sublength = 0;
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            while(sum >= target) {
                sublength = (j - i + 1);
                result = result < sublength? result: sublength;
                sum -= nums[i++];
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

59.螺旋矩阵II

https://leetcode.cn/problems/spiral-matrix-ii/

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

示例 1:

!https://assets.leetcode.com/uploads/2020/11/13/spiraln.jpg

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

思考:这道题 就模拟题。之前我记得主要是考察边界的问题。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0));
        int startx = 0, starty = 0;
        int loop = n / 2;
        int mid = n / 2;
        int count = 1;
        int offset = 1;
        int i , j;
        while(loop--) {
            i = startx;
            j = starty;

            for (j = starty; j < n - offset; j++) {
                res[startx][j] = count++;
            }
            for (i = startx; i < n - offset; i++) {
                res[i][j] = count++;
            }
            for (; j > starty; j--) {
                res[i][j] = count++;
            }
            for (; i > startx; i--) {
                res[i][j] = count++;
            }
            startx++;
            starty++;
            offset += 1;
        }
        if (n % 2) {
            res[mid][mid] = count;
        }
        return res;
    }

};
  • 时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
  • 空间复杂度 O(1)

扩展题目

904. 水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

示例 2:

输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

思考:水果篮只能装单一类型水果。这道题可以抽象一下,最多只能有两种不同的数据,问最多的nums范围。可以用双指针法,在两个指针的中间最多只能有两种数据。遇到另一种数据则停止。可以用一个map来记录。

解题:

我们用哈希表 cntcntcnt 维护当前窗口内的水果种类以及对应的数量,用双指针 jjj 和 iii 维护窗口的左右边界。

遍历数组 fruits,将当前水果 xxx 加入窗口,即 cnt[x]++cnt[x]++cnt[x]++,然后判断当前窗口内的水果种类是否超过了 222 种,如果超过了 222 种,就需要将窗口的左边界 jjj 右移,直到窗口内的水果种类不超过 222 种为止。然后更新答案,即 ans=max⁡(ans,i−j+1)ans = \max(ans, i - j + 1)ans=max(ans,i−j+1)。

遍历结束后,即可得到最终的答案。

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        unordered_map<int, int> umap;
        int result = 0;
        for (int i = 0, j = 0; i < fruits.size(); i++) {
            umap[fruits[i]]++;                            //记录水果种类及数量
            while(umap.size() > 2) {
                int temp = fruits[j++];
                if (--umap[temp] == 0) umap.erase(temp);  // 当水果数量为0的时候,就可以将其擦除了
            }
            result = max(result, i - j + 1);
        }
        return result;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

思考:用一个unordered_map 哈希表来记录t中所有字符的出现次数。然后用双指针在s中寻找满足条件的子串。

解题:两个哈希表,hs哈希表维护的是s字符串中滑动窗口中各个字符出现多少次,ht哈希表维护的是t字符串各个字符出现多少次。如果hs哈希表中包含ht哈希表中的所有字符,并且对应的个数都不小于ht哈希表中各个字符的个数,那么说明当前的窗口是可行的,可行中的长度最短的滑动窗口就是答案。

过程如下:

1、遍历t字符串,用ht哈希表记录t字符串各个字符出现的次数。

2、定义两个指针j和i,j指针用于收缩窗口,i指针用于延伸窗口,则区间[j,i]表示当前滑动窗口。首先让i和j指针都指向字符串s开头,然后枚举整个字符串s ,枚举过程中,不断增加i使滑动窗口增大,相当于向右扩展滑动窗口。

3、每次向右扩展滑动窗口一步,将s[i]加入滑动窗口中,而新加入了s[i],相当于滑动窗口维护的字符数加一,即hs[s[i]]++

4、对于新加入的字符s[i],如果hs[s[i]] <= ht[s[i]],说明当前新加入的字符s[i]是必需的,且还未到达字符串t所要求的数量。我们还需要事先定义一个cnt变量, cnt维护的是s字符串[j,i]区间中满足t字符串的元素的个数,记录相对应字符的总数。新加入的字符s[i]必需,则cnt++。

5、我们向右扩展滑动窗口的同时也不能忘记收缩滑动窗口。因此当hs[s[j]] > ht[s[j]时,说明hs哈希表中s[j]的数量多于ht哈希表中s[j]的数量,此时我们就需要向右收缩滑动窗口,j++并使hs[s[j]]–,即hs[s[j ++ ]] --。

6、当cnt == t.size时,说明此时滑动窗口包含符串 t 的全部字符。我们重复上述过程找到最小窗口即为答案。

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char, int> hs, ht;
        for (auto c: t) ht[c]++;
        string result;
        int count = 0;
        for (int i = 0, j = 0; i < s.size(); i++) {
            hs[s[i]]++; 
            if (hs[s[i]] <= ht[s[i]]) count++;     //记录必须加入的字符长度
            while(hs[s[j]] > ht[s[j]]) hs[s[j++]]--;      // 右移j,并将hs中的计数-1
            if (count == t.size()) {      //当包含了t中所有的字符
                if (result.empty() || i - j + 1 < result.size()) 
                    result = s.substr(j, i - j + 1);      //分割子字符串
            }
        }
        return result;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

54. 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例 1:

!https://assets.leetcode.com/uploads/2020/11/13/spiral1.jpg

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

思考:这道题也是一道模拟题,主要考察对代码的掌控能力。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> result;
        if (matrix.empty()) return result;
        int u = 0;
        int d = matrix.size() - 1;
        int l = 0;
        int r = matrix[0].size() - 1;
        while (true) {
            for (int i = l; i <= r; i++) result.push_back(matrix[u][i]);
            if (++ u > d) break;
            for (int i = u; i <= d; i++) result.push_back(matrix[i][r]);
            if (-- r < l) break;
            for (int i = r; i >= l; i--) result.push_back(matrix[d][i]);
            if (--d < u) break;
            for (int i = d; i >= u; i--) result.push_back(matrix[i][l]);
            if (++l > r) break;
        }
        return result;
    }
};

复盘总结

个人反思

双指针方法yyds

模拟题的话只能是找到规律来做了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值