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

1.有序数组的平方

代码:(暴力解法)

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

代码: (双指针)

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

note:

        做这道题的时候一直纠结于怎么找到正负交界,只要找到正负交界就可以从这里出发去从绝对值小的地方出发,去比较着来填新数组了。

        但其实,我完全可以找数组的起点和终点,它们一定分布着绝对值最大的元素。因此,只要用双指针从数组两端一起向中间遍历就好了。然后谁的平方大,就把谁填到新数组的末尾。 

2.长度最小的子数组

代码: (暴力法,超时)

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum = 0;
        int result = INT_MAX;
        for(int i = 0; i < nums.size(); i++){
            sum = 0;
            for(int j = i; j < nums.size(); j++){
                sum += nums[j];
                if(sum >= target){
                    int sublength = j - i + 1;
                    result = min(result,sublength);
                    break;
                }
            }
        }
        if(result == INT_MAX) return 0;
        return result;
    }
};

代码:双指针(滑动窗口)

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum = 0;
        int result = INT_MAX;
        int i = 0; // 窗口的起始位置
        for(int j = 0; j < nums.size(); j++){ // 只能去遍历窗口的终止位置
            sum += nums[j];
            while(sum >= target){
                int sublength = j - i + 1;
                result = min(result,sublength);
                sum -= nums[i];
                i++;
            }
        }
        if(result == INT_MAX) return 0;
        return result;
    }
};

note:关键就是记住——滑动窗口固定的是右边界!!因为只是固定了起始位置,终止位置就还得再需要一个循环。

        当我们的总和不小于target时,就是缩小我们窗口的时机。 

相关题目练习: 

 904. 水果成篮 - 力扣(LeetCode)

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        unordered_map<int,int> cnt; // 记录水果种类和数目的映射关系
        int left = 0;
        int result = 0;
        for(int right = 0; right < fruits.size(); right++){
            cnt[fruits[right]]++;
            while(cnt.size() > 2){
                auto it = cnt.find(fruits[left]);
                it->second--;
                if(it->second == 0){
                    cnt.erase(it);
                }
                left++;
            }
            result = max(right - left + 1,result);
        }
        return result;
    }
};

note:

        首先是自己根本不知道怎么用这个滑动窗口。我觉得规律是,滑动窗口是在满足一个要求的情况下,尽量去追求另一种要求(内层的while循环)。 这道题是尽量追求数量多的水果,在这个前提下,当不满足水果种类在2以内时,把左边界的水果剔除出去。

         这道题犯了好多低级错误,首先是erase拼写错误,还有内层循环的剔除的左边界的元素写成右边界了。。

76. 最小覆盖子串 - 力扣(LeetCode)

class Solution {
private:
    unordered_map<char,int> map_window,map_base;
    bool check(){
        for(auto &it : map_base){
            if(map_window[it.first] < it.second){
                return false;
            }
        }
        return true;
    }
public:
    string minWindow(string s, string t) {
        for(char c:t){
            map_base[c]++;
        }
        int left = 0;
        int result = INT_MAX;
        int res_start_index = -1;
        for(int right = 0; right < s.size(); right++){
            map_window[s[right]]++;
            while(check()){
                // 更新结果
                if(right - left + 1 < result){
                    result = right - left + 1;
                    res_start_index = left;  // 记录该覆盖子串的起始索引。最后直接通过起始索引+最小长度来求结果,避免这里重复的拷贝复制
                }
                }
                // 缩小窗口
                map_window[s[left]]--;
                left++;
            }
        }
        if(res_start_index == -1) return "";
        return s.substr(res_start_index,result);
    }
};

 note:脑壳疼。

        主要难点在于,如何判断“覆盖”。

s 的子串 BANC 中每个字符的个数,都大于等于 t=ABC 中每个字符的个数,这就叫「覆盖」。其实看字面意思就能理解,这里之所以专门强调下,是为了将「覆盖」转化为数学表达式,为的是方便转为代码语言。

作者:sca
链接:https://leetcode.cn/problems/minimum-window-substring/solutions/2796027/jie-he-ge-wei-da-lao-ti-jie-xie-ge-ge-re-d40w/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.螺旋矩阵2

代码: 

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int loop = n / 2;
        int mid = n / 2;
        int offset = 0;
        int value = 1;
        vector<vector<int>> matrix(n,vector<int>(n,0));
        while(loop--){ // 左闭右开
            int i,j;
            for(j = offset; j < n - 1 - offset; j++){
                matrix[offset][j] = value++;
            }
            for(i = offset; i < n - 1 - offset; i++){
                matrix[i][n - 1 - offset] = value++;
            }
            for(j = n - 1 - offset; j > offset; j--){
                matrix[n - 1 - offset][j] = value++;
            }
            for(i = n - 1 - offset; i > offset; i--){
                matrix[i][offset] = value++;
            }
            offset++;
        }
        if(n % 2) matrix[mid][mid] = value;
        return matrix;
    }
};

note:

        这次我直接用offset来确定每次绕圈的边界值,具体的确定方式其实很简单——就是用第一圈来看,哪个的坐标是小的直接写成offset,如果是大的边界直接写成 n - 1 - offset就好了。(感觉和以前做的数学应用题很像。。) 

相关题目练习

 54. 螺旋矩阵 - 力扣(LeetCode)

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        // 左闭右开
        int m = matrix.size();
        int n = matrix[0].size();
        int cnt = 0;
        vector<int> result(m * n, 0);
        int offset = 0;
        int loop = min(m,n) / 2;
        int mid = loop;
        while(loop--){
            for(int j = offset; j < n - 1 - offset; j++){
                result[cnt++] = matrix[offset][j];
            }
            for(int i = offset; i < m - 1 - offset; i++){
                result[cnt++] = matrix[i][n - 1 - offset];
            }
            for(int j = n - 1 - offset; j > offset; j--){
                result[cnt++] = matrix[m - 1 - offset][j];
            }
            for(int i = m - 1 - offset; i > offset; i--){
                result[cnt++] = matrix[i][offset];
            }
            offset++;
        }
        if(m == n && m % 2){
            result[cnt] = matrix[mid][mid]; 
        }
        if(m < n && m % 2){
            for(int j = offset; j <= n - 1 - offset; j++){
                result[cnt++] = matrix[offset][j];
            }
        }else if(n < m && n % 2){
            for(int i = offset; i <= m - 1 - offset; i++){
                result[cnt++] = matrix[i][offset];
            }
        }
        return result;
    }
};

note:思路和上面的一样,圈数由矩形的短的那条边决定,通过offset来调整每次转圈的起点和终点。我的错误就是写0写习惯了。。。第三四个循环应该是和offset比较,我写成大于0了。。

转完圈后,还要看较短边是不是二的倍数,如果不是我们还是需要额外地遍历中间的元素。

 

 LCR 146. 螺旋遍历二维数组 - 力扣(LeetCode)

这题和上题一样,就不写了。 

4.总结 

文章链接:代码随想录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值