2.数组(2) | 有序数组的平方、长度最小的子数组、螺旋矩阵II

这篇博客记录了在解决编程题目时遇到的挑战,包括使用双指针对有序数组进行平方操作以保持非递减顺序,寻找数组中最小子数组和的长度,以及生成螺旋矩阵。博主详细分析了每道题目的关键点,如循环不变量和指针移动策略,并通过不断调整和学习,最终实现了正确的解决方案。
摘要由CSDN通过智能技术生成

        这3个题前2个还是关于数组中双指针的用法,第3个是二维数组的循环控制。3个题也都提前看过了视频,但还是没有一次性AC,再次说明还不细心,掌握也不扎实。


·        第1道题(977.有序数组的平方)的关键在于,认识到因为数组可能左端是绝对值较大的负数,右边是绝对值较大的正数,所以数组的平方是两边大中间小的。了解这一点后就可以用双指针实现O(n)即用左右两个指针分别从左,右向中心递进,每次都取平方数较大的值新添加进新数组,就可以得到一个非递增的答案,但题目要求的是一个非递减的数组。第1次提交答案错误是由于忘记数组的绝对值是两边大中间小,而非两边小中间大,于是把循环中的第1个if条件错写成l2 < l1;将 < 变为 > 后,结果仍然不对,这是因为忘记了前面提到的题目要求(要求非递增,而不是非递减),于是需要将答案数组的填充改为逆序。然而函数的返回值是vector,自己知道的vector只能正向填充,而又不想将填充完的vector又继续填进新vector,忘记了视频中的做法,于是不会了。经学习得知vector初始化时可以定义大小和初始填充的内容,分别就是前两个参数。也查到另外一种做法就是在得到答案vector后sort()一下,写法为sort(vector.begin(), vector.end()),但如果这样的话sort()的复杂度就已经超过O(n)了。更改后AC。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int l = 0, r = nums.size() - 1;
        vector<int> ans(nums.size(), 0);
        int ind = nums.size() - 1;
        while (l <= r) {
            int l2 = nums[l] * nums[l], r2 = nums[r] * nums[r];
            if (l2 > r2) {
                ans[ind--] = l2;
                ++l;
            }
            else {
                ans[ind--] = r2;
                --r;
            }
        }
        return ans;
    }
};

        二刷

        用了比较麻烦的方法——初始化要返回的数组为空vector,然后逐个push_back()后再对vector进行reverse()。可以像上面一刷时,初始化要返回的数组时就设置好大小,然后反向填充即可。


        第2道题(209.长度最小的子数组)在不止一个笔试题里遇到过类似的题目,看过视频解法看上去简单,实则有很多细节,尤其是在指针右移的时机方面。

  1. 首先是外部while()循环中的运行条件,需要保证fast指针不越界。
  2. 其次内部要有两个while(),两者的次序很重要,因为题目需要达到sum >= target的目的,所以应该首先进行fast右移,sum不断增加的循环,达到条件后再,进行slow右移,sum减小的循环。
  3. 两个内部循环各自的运行条件中,除了要保证sum与target的大小关系外,还要保证指针也不越界(吸取day 1中题目的经验)。
  4. 每个内部循环中,都应该首先更改sum,再右移指针,而非相反,否则数组中第1个元素会被漏掉。
  5. 两个内部循环结束后,fast应该处于滑动窗口最右侧+1的位置, Slow应该处于滑动窗口最左侧的位置,所以长度应该是fast - slow + 1。
  6. 题目要求没找到的情况下返回0,同时又因为需要找长度最小的子数组,所以不能将最小程度初始化为0,解决方法是设定变量find初始化为false,并在函数进入到第2个内部循环后将其设置为true,根据函数是否进入到第2个内部循环来判断是否成功。
    class Solution {
    public:
        int minSubArrayLen(int target, vector<int>& nums) {
            int slow = 0, fast = 0;
            int sum = 0, ans = INT_MAX;
            bool find = false;
            while (fast < nums.size()) {
                while (fast < nums.size() && sum < target) {
                    sum += nums[fast];
                    ++fast;
                }
                while (slow <= fast && sum >= target) {
                    find = true;
                    sum -= nums[slow];
                    ++slow;
                }
                ans = min(ans, fast - slow + 1);
            }
            if (!find) {
                ans = 0;
            }
            return ans;
        }
    };

        二刷

  1. 内层循环的fast右移没写越界判断,导致越界错误;
  2. 没有写“整个数组的和仍不够大,则需要返回0”的情况;
  3. 对1的处理错写为“最小长度 == 数组长度”(因为最小长度最长也是“数组长度 - 1”,所以不可能与数组长度想等)。改为“left == 0”后正确如下:
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int ans = INT_MAX;
        int left = 0, right = 0;
        int sum = 0;
        while (right < nums.size()) {
            while (right < nums.size() && sum < target) {
                sum += nums[right++];
            }
            while (sum >= target) {
                sum -= nums[left++];
            }
            ans = min(ans, right - left + 1);
        }
        return left == 0 && sum < target ? 0 : ans;
    }
};

        但如果用一刷时“如果进入slow右移则说明数组和够大”的判断条件会更直观。


        第3道题(59.螺旋矩阵II)的关键在于找到循环不变量,这一题的循环不变量在于每个外层循环填充一圈,每个内层循环填充一条边上的n - 1个数字,总共4个内层循环,填充一圈4n - 4个数字。主要问题在于如何设置循环中的数组下标,每个内部循环都是行下标不变,移动列下标,或列下标不变,移动行下标,且移动下标范围的最小值和最大值固定,所以用每次外部循环中设置行起始下标、结束下标,列起始下标、结束下标这4个变量的方式来控制4个循环。

        在一开始就遇到了需要建立一个已知大小的2维vector的问题,回顾学习后发现写法是

vector< vector<int> > mat(m, vector<int> (n, 0));

其中0表示初始化其中所有值为0,m,n分别是2维数组的行数和列数。之后需要的是仔细,否则循环很容易出错。果不其然我就出错了,最后一个内部循环中错写成了

mat[j][cEnd] = num++; // 错误,应该是cStart,不是cEnd

改正后AC。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> mat(n, vector<int>(n));
        int len = n - 1;
        int num = 1;
        int rBegin = 0, rEnd = n - 1, cBegin = 0, cEnd = n - 1;
        while (len > 0) {
            for (int j = cBegin; j <= cEnd - 1; ++j) {
                mat[rBegin][j] = num++;
            }
            for (int i = rBegin; i <= rEnd - 1; ++i) {
                mat[i][cEnd] = num++;
            }
            for (int j = cEnd; j >= cBegin + 1; --j) {
                mat[rEnd][j] = num++;
            }
            for (int i = rEnd; i >= rBegin + 1; --i) {
                mat[i][cBegin] = num++;
            }
            len -= 2;
            rBegin++, rEnd--;
            cBegin++, cEnd--;
        }
        if (n % 2 != 0) {
            mat[n / 2][n / 2] = num;
        }
        return mat;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值