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

文章讨论了三个编程问题的解题思路,包括有序数组的平方排序,使用双指针和暴力解法,以及滑动窗口解决最小连续子数组问题,最后介绍了构建螺旋矩阵的算法,强调了循环不变量原则和时间复杂度优化。

第二天刷题,在做之前能有一点点思路,但是和答完还是有差距。

977.有序数组的平方

题目链接

视频链接

将一个包含正负数的有序数组  的平方进行排序。

思路:

先用一个for循环逐个平方,然后排序。

看完代码随想录之后的思路:

暴力解法:

与我的思路相同,最后排序使用C++内置函数sort。但是时间复杂度较高,为O(n + nlogn)。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int slow=0;
        int fast=0;
        for(int i=0;i<nums.size();i++)
        {
            nums[i]=nums[i]*nums[i];
        }
        sort(nums.begin(),nums.end());
        return nums;
    }
};

双指针解法:

创建一个新数组,当左指针小于等于右指针时,循环比较左右指针的平方数,大的那个数从右往左填入新数组中。时间复杂度为O(n)。

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

遇到的困难:

for循环中,任何一个语句都可以不写,但是分号一定要有

vector的定义中第一个参数是个数,第二个参数是所有元素用什么填充。

双指针法:for循环的条件语句中,i必须<=j,否则最后一个元素无法写入。

收获:

vector数组是一个能存放任意数据类型(类,结构,普通变量类型等)的动态数组!,在数据结构中就相当于顺序储存的线性表,寻找元素非常快,但是插入元素的时间却很大(list是一个双向链表,在同一个为止插入大量的数据时速度很快,但是查找的速度就会慢很多)

for(初始化语句;判断条件语句;控制条件语句)

{ 循环体语句; }

for循环中的初始化语句和控制条件语句的变量可以不同,只要逻辑不出错可以自行,但是不建议这么写,如果控制条件语句的变量不同,最好放在循环体语句中。

重点:

for循环的条件语句中小于等于必须写上,否则无法将最后一个元素写入新数组中——等于号是否取主要看最后1~2个元素的情况。

209.长度最小的子数组

找出数组中大于等于目标值的最小连续子数组。

题目链接

视频链接

思路:

双层循环

看完代码随想录之后的思路:

暴力解法:双循环,外层循环从头到尾找开头,内存循环从当前数值开始,向后依次查找和什么时候大于target。当大于一次,马上终止,进入下一个开头。时间复杂度为O(n^2),空间为O(1)。

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

滑动窗口法:先找窗口的右界,然后再缩小左界的范围。这样搜索的效率快很多。评价标准是时间复杂度为O(n);直观感受是,在第一次找右界的过程中,已经把前面的序列求和过了一次,尽力减少求左界的次数,避免了暴力解法中每次重复求和的过程。空间复杂度同样为O(1)。

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

遇到的困难:

暴力解法中:

  • INT32_MAX是最大值,求最短数组,用最大值和更新值比较,这样就避免了第一次需要用更新值赋值的情况。
  • 内层循环中,j-i+1直接可作为子数组长度,不需要每次循环加一来算。
  • if+赋值的简要写法:等号后先写条件,问号之后写赋予的值,两种赋值中间用冒号隔开。

滑动窗口解法中:

  • 内层while的等于号:相等时,先更新序列长度,再更新和的大小,最后移左界。等号是否取,先看取等时要求的最重要的一步是否执行。不要把下一步混入其中。
  • 执行时,先执行要求的任务,最后更新指针的位置。(我把while中的求和写在了更新长度和求result中间)
  • 所有变量必须赋初值,不能只定义,不写初值。

收获:

带有随时更新的题,一定先准备两个数字。一个在随时在更新,一个记住当前更新值之中的最值。

循环中,当前的任务和下一次的任务不要放在一起看;不要因为等号就特殊,把等号同样看做是要求的一部分,它具有和不等号一样的效应。

重点:

滑动窗口最重要的是先求右界。

59.螺旋矩阵II

给定一个n,写出一个n^2的螺旋矩阵,从1到n一次填入

题目链接

视频链接

思路:

看完代码随想录之后的思路:

计算出所需要的圈数,每圈四个循环,将每边的数一次填入。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0));
        int startx,starty=0;
        int loop=n/2;
        int mid=n/2;
        int offset=1;
        int count=1;
        int i,j=0;
        while(loop--)
        {
            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>starty;j--)
            {
                res[i][j]=count;
                count++;
            }
            for(;i>startx;i--)
            {
                res[i][j]=count;
                count++;
            }
            startx++;
            starty++;
            offset+=1;
        }
        if(n%2==1)
        {
            res[n/2][n/2]=count;
        }
        return res;
    }
};

遇到的困难:

  • 所有设置的变量,一定要注意在写入的过程中,变量发生了什么变化;在下一次的循环中,需要对该变量进行什么操作。第一次写的时候startx、starty都忘记了自增。
  • ++放在变量后面,先进行赋值,在进行自增。
  • 注意每边循环时的起始条件和限定条件,以及赋初值时的坐标指向。
  • 容器初始化:

一维设置数组长度,给定值初始,N 为默认数组长度,全部元素设置为初始值 value

vector<int> data(N,value);

二维设置数组长度,给定值初始,N1 为行,N2 为列,默认值为 value

vector<vector<int>> data(N1, vector<int>(N2,value));

收获:

函数的定义:

类型标识符 函数名(形式参数列表)

{

        变量声明

        语句

}

类型标识符:用来标识函数的返回值类型。这里定义为二维容器。

while的条件,非0即为真,可以用下列第一种方法替换第二种方法:

while(loop--)
{

}

while(loop>=1)
{
    loop--;
}

重点:

循环不变量原则:注意每一次循环中确保边界条件统一。(4个边都是左闭右开)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值