代码随想录算法训练营第二天| LeetCode977. 有序数组的平方、27. 移除元素、59.螺旋矩阵2

977.有序数组的平方

题目链接:力扣

解析:

分析题目意思我们可以发现,题目给到的是一个 具有 正负有序序列,题目让我们按照平方来再排序,也就是我们需要考虑正负两种情况下的平方。

因此本道题,我们采取双指针+减而治之的算法思想。绝对值最大的两个数,一定位于向量的末尾或者开头,因此每一次比较我门都能赛出平方后最大的那个数,此时必定能将问题规模减去1。

为了方便的记录平方后的结果,我在这里重新开辟了一个跟原向量一样大小的空间。

因此最后的时间复杂度与空间复杂度均为O(n)。如果你有兴趣,也可以继续尝试将此算法改造成就地算法。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
     vector<int> nums_1 = nums;
     int hi = nums_1.size() - 1;
     int lo = 0;
     int pos = nums_1.size() - 1;
     while (lo < hi){
         if(abs(nums_1[lo]) < abs(nums_1[hi])){
             nums[pos--] = nums_1[hi] * nums_1[hi];
             hi--;
         }
         else{
             nums[pos--] = nums_1[lo] * nums_1[lo];
             lo++;
         }
     }
     nums[pos] = nums_1[lo] * nums_1[lo]; //lo hi相会 记录最后一次结果,此次结果也必定是最小的
     return nums;
    }
};

209.长度最小的子数组

题目链接:力扣

解析:

分析题意可得,给出的向量非有序但非负,希望输出的结果是满足 >= target 的最短区间长度。如果熟悉计算机网络的同学,肯定对滑动窗口机制再为熟悉不过了,而本道题也是采用了类似的思想。

首先我们构造两个指针,首指针不动,尾指针向后移并不断累加,滑动窗口一直变大,直至找到第一个区间;记录及更新最小长度后,首指针向前移动,窗口首位置往前移动,并将移动的数值从sum中减掉。

此时分为两种情况:

  • 收缩一格后,sum >= target,则此时继续更新min,首指针再向前一格,窗口向后收缩
  • 收缩一格后,sum < target,则此时尾指针向后扩张,窗口向后扩张

按照此思路一直滑动窗口,若该次窗口满足,则比较且更新最短区间长度。我们可以一直移动至向量最后,此时便可得到最短区间长度。

注:最短的区间长度不会小于1,此时刚好有一个值 >= target,此时已经得到我们最想要的结果,因此直接返回即可,无需继续滑动窗口。

根据此思路,窗口的lo与hi均单向扩张,不会回溯,且无需开辟额外空间,因此时间复杂度为O(n),空间复杂度为O(1)。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int min = 0;
        int lo = 0;
        int sum = 0;
        int hi = lo; // 双指针 hi与 lo ,sum记录区间的总和 ,min记录最小区间长
        while(hi < nums.size() && lo < nums.size()){
            sum += nums[hi];
            if(sum >= target){
                int temp =  hi - lo + 1; // 区间长度记得加一
                if(min == 0){
                    min = temp;
                }
                else if(temp < min){
                    min = temp;
                }
                if(min == 1) {
                    return min; // 由题意可知,最短的区间就是单个值大于等于target,此时直接返回即可
                }
                sum -= nums[lo]; // 找到符合要求的一段区间 记录完毕min后 将区间首指针向前移位,同时sum减掉首值
                sum -= nums[hi];// 减去尾值,因为再循环一次时会加回来
                lo++; // 首指针减完再前移
            }
            else{
                hi++;// 若小于目标则尾指针向后移动
            }
        }
        return min;
        } 
};

59.螺旋矩阵II

题目链接:力扣

解析:

该题是一个矩阵打印的题目,主要涉及的是循环体的构造及边界条件的确定。

核心思想仍然是减治的思想,通过一圈一圈的赋值,让矩阵规模不断缩小,问题规模也随之缩小。

赋值分为四种情形:

  1. 上 边+ 从左至右
  2. 右 边+ 从上至下
  3. 下 边+ 从右至左
  4. 左 边+ 从下往上 

按照此顺序,不断循环。需要注意的是,矩阵右四个角,每个角上的顶点被两条边共享,本算法将相交点均交予 下一条边处理,也就是将上一条边的末尾作为下一条边的起始。

注意:边长n为奇偶时,最后一步不一样。n为偶数时,可以通过n/2轮,对于矩阵循环顺序赋值将问题解决;而n为奇数时,会剩余中间一个格子,此时需额外赋值一次

根据本算法,每个格子被遍历与赋值一次,总共n^2个格子,因此时间复杂度为O(n^2)【已是此类问题最好的时间复杂度了】,空间复杂度为O(1)

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> nums(n, vector<int>(n, 0));

        int start_x = 0;
        int start_y = 0;//每轮起始位置
        int offset = 1;//每轮结束位置向前偏移量

        int round = n/2;// 逆时针循环转圈轮次
        int count = 1; //矩阵赋值计数

        while(round){  

            int i = start_x;
            int j = start_y;

            for(; j < n - offset; j++){  //从左至右
                nums[i][j] = count++;
            }

            for(; i < n - offset; i++){ //从上至下
                nums[i][j] = count++;
            }

            for(; j > start_y; j--){ //从右至左
                nums[i][j] = count++;
            }

            for(; i > start_x; i--){ //从下至上
                nums[i][j] = count++;
            }
            start_x++;
            start_y++;
            offset++; //一圈赋值结束,矩阵收缩
            round--;// 轮次减1
        }
        if(n % 2 == 1){
            nums[n/2][n/2] = n*n; //奇数情况的平方仍为奇数,意味着中间会剩余一个格子
        }
        return nums;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值