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

没 有明确的学习目的就不要在正在做一件事的时候打开B站,因为真的会迷失一段时间,看一堆没有意义的东西!!!
然后再回来做继续做的事就感觉脑袋被掏空。
某种意义上我称B站为我的大毒草!!
今天卡哥给留了三道题。。。
严重怀疑我这菜鸡会不会完成

977.有序数的平方

题目链接

1.暴力写法

自己思路是一样的,就是对容器毫无了解,之后用sort函数的时候不知道容器该咋用

在这里新学了容器的begin()和end()两个函数
又得重新复习一下sort

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;

    }
};

2.双指针法


卡哥文章讲解:
就是跟昨天在数组种删除val值差不多,
但是你得能想到这种情况能用双指针,我感觉就是对当前元素一边判断一边移动。

理论:
你平方之后,有些负数(在数组最前端)可能会变大,所以最值都会出现在数组两端,用两个指针,分别从两两边向中间走,选出更大的元素然后填入新的数组中,(有点类似于快排啊)
容器开辟数组的代码:vector result(A.size(), 0);
可真得是学习学习容器了。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i=0;i<nums.size();i++){
            nums[i]=nums[i]*nums[i];
        }
        int k=nums.size()-1;
       vector<int> result(nums.size(), 0);
       for(int i=0,j=nums.size()-1;i<=j;){
       //注意此处的for循环的第二个分号别忘了。
       //同时也要注意是i<=j,别忘了“=”
       //第三出可以没有,可以在函数体中体现
            if(nums[i]<=nums[j])result[k--]=nums[j--];
            else result[k--]=nums[i++];
       }
        return result;
    }
};

又重新敲了一遍,明天敲新的算法之前再复习一遍

209.长度最小的子数组


弱弱的吐槽一句我这都得让人监督着学习可咋好
监督太过反而逆反,我这样没救了

题目链接
首先注意审题,是找最短的连续的子数组,满足其元素之和大于所给target

暴力写法

思路:
先找一个变量存要找的子数组的长度,一开始赋值为0
int sublength=0;
要找一个子数组的话,就得有始有终,然后寻找下一个子数组,有点像kmp那个最基础版。
所以你得有一个头指针,用于定位子数组开头,然后寻找每个新的子数组之前得往右移一个,
尾指针,就是不断从头指针的位置往后加数,直到这个子数组的所有元素之和已经大于target就开始停止,若其长度已经比已经所找到的还要短,那么就更新sublength。

第一次执行,忘了一个问题,就是length不能一开始就赋值0,
而且break放if条件之外了,呵呵,直接一工作即躺平。
错误代码示例

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

最后还比较个啥,0肯定最小啊
从卡哥代码上抄了个最大赋值INT32_MAX
在这里插入图片描述

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

暴力解超出时间限制了。时间复杂度为O(n^2)。
可我连暴力解也得好好想想。。。

滑动窗口

卡哥滑动窗口教程

看图解有点像kmp字符串比对
我感觉像以前以后俩指针后面那个指针不断往后移动,如果子数组元素之和>=target,那么就已经找到了当前最短的子数组,然后前指针往后移一位,接着寻找新的子数组。

这里有个注意事项,你前指针往后移动了一个的时候,后指针是没有动的,所以,此处就会涉及一个sum=sum-nums[i++];
可以直接算出新的子数组和
,而不需要再用一个for遍历新的子数组了

后面卡哥说滑动窗口也是双指针的一种,果然我的感觉没有出错~

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
    int sum=0,sublength=INT32_MAX,length=0,i=0;
    for(int j=0;j<nums.size();j++){
        sum+=nums[j];
        
        while(sum>=target){length=j-i+1;
        sublength=sublength<length?sublength:length;
        sum-=nums[i++];//找到满足条件的子数组后变更
        //这步让双指针看起来更像滑动窗口。
        }   
            
    }
    return  sublength==INT32_MAX?0:sublength;
    }
};
时间复杂度:O(n)
空间复杂度:O(1)

线性关系不是平方关系,
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)

(我就纳闷之前任务清单上写的那个拓展题在哪,今天终于看见了。。。)

我再敲一遍。

59.螺旋矩阵II


题目链接

一看题,要不是我花钱了,这题我没有什么理由自己去敲。
犹如自己花钱给自己开工资的感觉。。。

循环不变量原则,昨天没学,今天补上,看着那个解说就很复杂的样子我很头疼。

这代码又敲了半天,我只能说,我还是前一天提前预习,多完成点任务比较好,当天完成有点有压迫感。

就是对边界处理要统一,比如你每个循环中要始终坚持左开右闭或左闭右开这一种原则就可
下面又来细节讲一下什么是左闭右开,就是只处理第一个边界点,而不处理最后一个边界点。

如何用容器定义一个二维数组vector>数组名字(行数,每行的一维数组)
还有涉及一个转几圈怎么确定呢? n/2圈,如果n是奇数,则中间个会留下来,要另外赋值,赋最大的那个值n*n

可能思路并不是最难理解的,是需要初始化几个变量,每个变量用来干啥比较难想。

第一次提交犯了两个错误,count一开始是从1 开始赋值的
有种顺时针给九宫格农田播种的感觉。。。
再一个就是在每轮的每边循环的过程中,就第一次是行坐标是用常量startx,紧接着
到下一个边用的就是遍历上次的边的列坐标j而不是starty了
而到了第三条边数组遍历摆脱了startx和starty

难点就是那些个变量,该怎么想,以及循环的过程中的边界处理与指针选择

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>>res(n,vector<int>(n,0));
        int startx=0,starty=0;
        int mid=n/2;
        int count=1;
        int loop=n/2;//规定所转的圈数(是否还要继续循环的条件)
        int offset=1;//用来限制每圈的边的长度(越往内圈你要走的边的长度越来越少)
        int i,j;
        while(loop--){
            i=startx;
            j=starty;
            for(j=starty;j<n-offset;j++){
                res[startx][j]=count++;
            }
            for(i=startx;i<n-offset;i++){
                res[i][j]=count++;
            }
            for(;j>starty;j--){
                res[i][j]=count++;
            }
            for(;i>startx;i--){
                res[i][j]=count++;
            }
            //第二圈的时候初始位置改变,每圈的边的长度变小了
            startx++;
            starty++;
            offset++;
        }
    //遍历完所有圈之后,如果是奇数,那得给中间赋值。
    if(n%2){
        res[mid][mid]=count;

    }
        return res;
    }
      
};

总结


1.对双指针算法又加深了理解,其中还有个新名字滑动窗口,适用于找出连续的子数列。
2.用到了如何用容器新建一维,二维数组,begin(),end();
3.了解了赋最大值INT32_MAX的用法;
4.进一步理解了循环不变量原则,做人做事要始终如一

咳,新任务都发出来了我刚给这仨题搞完,对前途抱有一阵渺茫啊。

你可能感兴趣的:(代码随想录,算法,矩阵,leetcode,数组,c++)