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

螺旋矩阵

59. 螺旋矩阵 II

根据第一次循环去写样例,但要记得其中的条件要设置成之后也要使用的循环变量

【用0作为边界条件判断,忽略了循环量】

代码如下,已经详细注释了每一步的目的,可以看出while循环里判断的情况是很多的,代码里处理的原则也是统一的左闭右开。

/*
 * @lc app=leetcode.cn id=59 lang=cpp
 *
 * [59] 螺旋矩阵 II
 */

// @lc code=start
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        //判断有几行几列
        vector<vector<int>> res(n, vector<int>(n,0));
        //起始位置
        int startX = 0;
        int startY = 0;
        //循环次数
        int loop = n / 2;
        //每次循环边界-1
        int offset = 1;
        //赋值
        int count = 1;
        //中间位置
        int mid = n / 2;

        //旋转的各个边界如何判定
        while(loop--){
            //向右移动
            //坚持循环不变量原则 区间取左闭右开
            int i = 0;
            int j = 0;

            for(j = startY; j < n - offset; j++){
                res[startX][j] = count;
                count++;
            }

            //向下移动
            for(i = startX; i < n - offset; i++){
                res[i][j] = count;
                count++;
            }

            //向左移动
            // for(; j > 0; j--){
            //     res[i][j] = count;
            //     count++;
            // }
            //需要比初始值大,而不是0(只考虑了第一次循环)
            for(; j > startY; j--){
                res[i][j] = count;
                count++;
            }

            //向上移动
            for(; i > startX; i--){
                res[i][j] = count;
                count++;
            }
            //起始位置的添加要在下一次循环开始之前
            startX++;
            startY++;

            //每一次循环后,再下一次循环开始前需要修改的变量
            //都需要在这一次循环结束前进行修改
            offset++;
        }
        // //起始位置
        // startX++;
        // startY++;

        

        //奇数偶数判断中间值
        if(n % 2 != 0){
            res[mid][mid] = n * n;
        }

        return res;

    }
};
// @lc code=end
// 时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
// 空间复杂度 O(1)

大家还记得我们在这篇文章数组:每次遇到二分法,都是一看就会,一写就废 (opens new window)中讲解了二分法,提到如果要写出正确的二分法一定要坚持循环不变量原则

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

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

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

由外向内一圈一圈这么画下去。

可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海,从此offer是路人

这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。

那么我按照左闭右开的原则,来画一圈,大家看一下:

img

这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。

这也是坚持了每条边左闭右开的原则。

滑动窗口

209. 长度最小的子数组

暴力解法:两个for循环,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。

那么滑动窗口如何用一个for循环来完成这个操作呢。

如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?

此时难免再次陷入 暴力解法的怪圈。

所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。

实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        //滑动窗口也可以理解为双指针
        //但是slow永远追不上fast
        int slow = 0;
        int subLength = 0;
        int sum = 0;
        //加入result 筛选结果
        // int result = 0;因为初值要比sublength大,所以初值要赋最大值
        // INT32_MAX指32位有符号整数类型int的最大值 2^32-1
        int result = INT32_MAX;
        for(int fast = 0; fast < nums.size(); fast++){
            sum += nums[fast];
            //符合条件、进入判断
            while(sum >= target){
                //更新subLength
                subLength = fast - slow + 1;
                result = result < subLength ? result : subLength;
                //保存最小的窗口长度,然后移动前面指针,继续判断
                sum -= nums[slow];
                slow++;
            }
        }
        //如果不加入对于result对最大值的筛选,每次返回的都是最新的值,得加入一个对于历史值的筛选
        return result == INT32_MAX ? 0 : result;
    }
};

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

977. 有序数组的平方

重构一个数组存放结果,不需要在原地移动

双指针:前后两个指针比较结果,然后存放在新空间内,新空间从后向前存放元素。

因为两边平方后比较最大值,最大值在数组的两头,方便比较

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        //创建跟原数组长度一致的新数组存放
        vector<int> new_nums;
        int size = nums.size();
        new_nums.resize(size);

        //建立双指针
        int slow = 0;
        int fast = size - 1;
        int n = size - 1;
        //为何取到“=”没有考虑进去 当fast = slow的时候,该元素并不能被忽略
        //代码随想录:因为要处理最后两个元素
        while(fast >= slow){
            if(nums[slow] * nums[slow] >= nums[fast] * nums[fast]){
                new_nums[n--] = nums[slow] * nums[slow];
                slow++;
            }else if(nums[slow] * nums[slow] < nums[fast] * nums[fast]){
                new_nums[n--] = nums[fast] * nums[fast];
                fast--;
            }
        }
        return new_nums;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值