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


2023年3月2日 周四

2 第一章数组

977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II ,总结

建议大家先独立做题,然后看视频讲解,然后看文章讲解,然后在重新做一遍题,把题目AC,最后整理成今日当天的博客

拓展题目可以先不做


详细布置

977.有序数组的平方

【链接】(文章,视频,题目)

题目建议: 本题关键在于理解双指针思想

题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/
文章讲解:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html
视频讲解: https://www.bilibili.com/video/BV1QB4y1D7ep

【第一想法与实现(困难)】

  • 暴力解法,直接平方之后使用标准库自带的std::sort快排

  • 双指针,写起来也不麻烦,然后我自己会一个个emplace_back之后最后std::reverse一下。自己写的双指针时间空间都还不错,80~90

【看后想法】

  • 暴力解法,甚至连新数组也不申请,直接修改了原数组…

  • 不需要std::reverse,只要一开始初始化固定size,都给0就行了。然后从后往前幅值

【实现困难】

重写基本比较容易,哈哈

【自写代码】

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

【收获与时长】一小时多,复习了双指针。对于循环结束的条件,手动判断一下有没有等号


209.长度最小的子数组

【链接】(文章,视频,题目)

题目建议: 本题关键在于理解滑动窗口,这个滑动窗口看文字讲解 还挺难理解的,建议大家先看视频讲解。 拓展题目可以先不做。

题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/
文章讲解:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html
视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE

【第一想法与实现(困难)】

  • 直接想到暴力解法。

    • 看了答案发现,第二层for,还可以多一个break优化。因为当能更新最短子数组,就说明这个left之下已经取得最小长度了,不需要再变化right,因为虽然满足子数组和更大,但是长度只会更长。

    • INT32_MAX,表示越小越好的整形初始值

【看后想法】

滑动窗口类似双指针,只不过关注的是两个指针之间的内容,形成了滑动的“窗口”。关键是for循环的下标是终止的下标而不是初始下标(初始下标就又回到了暴力解法)。

【实现困难】注意需要一直移动左指针,是while不是if。while条件是 >= ,带着等号!

【自写代码】

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // 滑动窗口,左闭右闭,[i, j],for循环变量为终止下标j
        int i = 0;
        int sum = 0;
        int result = INT32_MAX;
        for(int j=0; j < nums.size(); j++) {
            sum+=nums.at(j);
            std::cout << "i:" << i << ",j:" << j << ",sum:" << sum << std::endl;
            // 如果子数组满足大于target条件,一直移动初始下标,看能否缩短
            while(sum >= target) {
                int sub_length = (j - i + 1);
                result = sub_length < result ? sub_length : result;
                std::cout << "sub_length:" << sub_length << ",result:" << result << ",sum:" << sum << std::endl;
                sum-=nums.at(i);
                ++i;
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};

【收获与时长】1.5小时,滑动窗口听过很多次,结构巧妙,强!


59.螺旋矩阵II

【链接】(文章,视频,题目)

题目建议: 本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。

题目链接:力扣 文章讲解:代码随想录 视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/

【第一想法与实现(困难)】想直接使用暴力解法,记录当前的left, right, top, bottom的位置。两边闭合区间。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        // 暴力,左闭右闭[left, rigth],上闭下闭[top, bottom]
        vector<vector<int>> res(n, vector<int>(n, 0));
        int left = 0, right = n-1, top = 0, bottom = n-1;
        int count = 0;
        int num = 1;
        // while(left <= right || bottom <= top) {
        while(num <= n * n) {
            if(count%4 == 0) { // top行,从左到右,++top
                for(int j = left; j <= right; ++j) {
                    res[top][j] = num++;
                }
                ++top;
            } else if(count%4 == 1) { // right列,从上到下,--right
                for(int i = top; i <= bottom; ++i) {
                    res[i][right] = num++;
                }
                --right;
            } else if(count%4 == 2) { // bottom行,从右到左,--bottom
                for(int j = right; j >= left; --j) {
                    res[bottom][j] = num++;
                }
                --bottom;
            } else if(count%4 == 3) { // left列,从下到上,
                for(int i = bottom; i <= top; --i) {
                    res[i][left] = num++;
                }
                ++left;
            } else {
                std::cout << "error" << std::endl;
            }
            ++count;
        }
        return res;
    }
};

出现问题runtime error:执行出错

Line 1034: Char 34: runtime error: addition of unsigned offset to 0x607000000020 overflowed to 0x607000000008 (stl_vector.h)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/…/lib/gcc/x86_64-linux-gnu/9/…/…/…/…/include/c++/9/bits/stl_vector.h:1043:34

overflowed是数组越界问题,调试方法,一段一段解注释,看哪里的边界条件写错了,最后发现是最后一个for循环的停止条件写错了。<=应为>=。

【看后想法】

而求解本题依然是要坚持循环不变量原则。

模拟顺时针画矩阵的过程:

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

卡哥的讲解基本与我想的一致,但是感觉左闭右开的写法比较麻烦。我个人感觉卡哥的写法需要处理的特殊情况比较多(奇数偶数,中间剩一个格),代码不够简洁优美。附上修改后自己的代码。

【实现困难】不写了,思路基本正确,我的写法更简洁。

【自写代码】

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        // 暴力,左闭右闭[left, rigth],上闭下闭[top, bottom]
        vector<vector<int>> res(n, vector<int>(n, 0));
        int left = 0, right = n-1, top = 0, bottom = n-1;
        int count = 0;
        int num = 1;
        while(num <= n * n) {
            if(count%4 == 0) { // top行,从左到右,++top
                for(int j = left; j <= right; ++j) {
                    res[top][j] = num++;
                }
                ++top;
            } 
            else if(count%4 == 1) { // right列,从上到下,--right
                for(int i = top; i <= bottom; ++i) {
                    res[i][right] = num++;
                }
                --right;
            }
            else if(count%4 == 2) { // bottom行,从右到左,--bottom
                for(int j = right; j >= left; --j) {
                    res[bottom][j] = num++;
                }
                --bottom;
            }
            else if(count%4 == 3) { // left列,从下到上,
                for(int i = bottom; i >= top; --i) {
                    res[i][left] = num++;
                }
                ++left;
            }
            ++count;
        }
        return res;
    }
};

【收获与时长】

  • 自己通过逐段解注释的方法来debug runtime error,

  • 数组,要坚持区间定义,循环不变量!


数组总结

题目建议:希望大家 也做一个自己 对数组专题的总结

文章链接:https://programmercarl.com/%E6%95%B0%E7%BB%84%E6%80%BB%E7%BB%93%E7%AF%87.html

坚持区间定义,循环不变量原则!

二分法——基础的区间定义,左闭右闭,左开右闭

删除元素——快慢指针,分别代表新数组元素(快)与下标(慢);相向双指针,待删除元素与须保留元素。

有序数组的平方——双指针,两头夹到中间

长度最小子数组——滑动窗口,终止位置做for循环变量,while判断而不是if,能短则短

螺旋矩阵——模拟行为。区间定义,循环不变量。模拟4个方向即可,注意不同方向的各种细节各不相同,初始值,终止条件,变化方向。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值