代码随想录算法训练营day02

题目:977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II

参考链接:代码随想录

977.有序数组的平方

思路:首先是暴力算法,先平方再排序,排序可以直接使用sort(),默认为快速排序,复杂度为O(nlogn)

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

然后是双指针法,即两个指针分别指向数组首尾,因为返回结果的最大值必定是从两边到中间,我们先定义一个结果数组ans。双指针每次遍历原数组时比较平方大小,大的则填写到ans的末尾,依次往前填充。复杂度O(n)

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len=nums.size();
        vector<int> ans(len,0);//初始化长度一样,开始全部置0
        int left=0,right=len-1,k=len-1;
        int tmp1,tmp2;
        while(left<=right && k>=0){
            tmp1=nums[left]*nums[left];
            tmp2=nums[right]*nums[right];
            if(tmp1>=tmp2){
                ans[k--]=tmp1;
                left++;
            }
            else{
                ans[k--]=tmp2;
                right--;
            }
        }
        return ans;
    }
};

运行结束后发现,时间为22ms,而暴力算法的时间为24ms,与标答对比,发现自己多定义了两个tmp,以及len,可能会导致力扣时间变长?然后while中的k>=0其实不需要,因为当双指针遍历完后必定恰好填满ans数组。

标答:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& A) {
        int k = A.size() - 1;
        vector<int> result(A.size(), 0);
        for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素
            if (A[i] * A[i] < A[j] * A[j])  {
                result[k--] = A[j] * A[j];
                j--;
            }
            else {
                result[k--] = A[i] * A[i];
                i++;
            }
        }
        return result;
    }
};

当然运行标答也只有20ms,基本没提升,所以力扣的运行时间看一乐就行。

209.长度最小的子数组

思路:首先想到暴力算法,即对nums中每一个元素,从其开始往后遍历,寻找和为target的子数组,这样的时间复杂度为O(n^2)。

滑动窗口算法:采用两个指针i和j,分别指向窗口头尾,for循环中使用j来遍历nums,对i的确定,可以这么看,由于nums都为正整数,如果目前窗口的和小于target,说明窗口需要扩大,则移动j,如果和大于等于target,说明窗口需要缩小,则移动i。这样复杂度O(n)

一开始错误代码:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len=nums.size();
        int i,j,n=len+1,ans=0,anslength;//n为最小子数组长度,初始化为len+1,ans为窗口的和
        for(i=0,j=0;j<len;j++){
            ans+=nums[j];
            //在一次for循环中,只移动i
            while(ans>=target){
                if(ans==target){//找到了子数组
                    anslength=j-i+1;
                    n=anslength<n ? anslength : n;
                }
                ans-=nums[i++];
            }
        }
        if(n!=len+1){
            return n;
        }
        return 0;
    }
};

发现自己一开始把题目看错了,题目是大于等于target,我看成了等于。去掉if就对了:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len=nums.size();
        int i,j,n=len+1,ans=0,anslength;//n为最小子数组长度,初始化为len+1,ans为窗口的和
        for(i=0,j=0;j<len;j++){
            ans+=nums[j];
            //在一次for循环中,只移动i
            while(ans>=target){
                anslength=j-i+1;
                n=anslength<n ? anslength : n;
                ans-=nums[i++];
            }
        }
        if(n!=len+1){
            return n;
        }
        return 0;
    }
};

本题的精髓在于while中的写法,滑动窗口左侧移动i,使得sum减小,一旦低于target则跳出while,移动j,又增大sum。标答中将n设置为INT32_MAX,也是一样的效果。

59.螺旋矩阵II

思路:本题我看到第一次想到的是分类讨论,将n为奇数和偶数分开讨论,当n为奇数的时候,设n=2k+1,这时候可以把矩阵分为k=1,k=2...这k次来遍历。当n为偶数的时候,设n=2k,同样是循环k次来遍历。例如n=4,k=2的遍历为123->456->789->101112,k=1的遍历为13->14->15->16,对k的一次循环则分4次进行,对四个边分别遍历k次。

一开始没写出来的代码:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> mat(n, vector<int>(n, 0));
        int k;
        int count=1;
        if(n%2==1){//n为奇数
            k=n/2;
            for(int s=k;s>0;s--){//对k反着遍历,每次循环填充4*(2k)个,s表示循环轮数
                for(int t=0;t<2*s;t++){
                    mat[][]=count++;
                    mat[][]=count++;
                    mat[][]=count++;
                    mat[][]=count++;
                }
            }
        }
        else{

        }
    }
};

看了解析后发现自己思路其实整体并没多大问题,主要是具体代码实现上,首先变量命名可以使用一点带有意义的单词比如loop,然后本题主要我没写出来的地方是对于每一个loop的遍历,需要分为四个for循环来写,我妄图一个for搞定四次,最后把自己搞混了也没写出来。

本题其实并没有设计什么深奥的算法,主要是对循环不变量的理解。

看完解析后自己写的结果:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> mat(n, vector<int>(n, 0));
        int i,j;
        int count=1;
        int loop=n/2;
        int mid=n/2;//n为奇数的时候单独处理一下
        int startx=0,starty=0;//每次开始循环时的起始位置
        int offset;
        for(;loop>0;loop--){
            i=startx;
            j=starty;
            offset=n/2+1-loop;//偏移量,每次循环都要加一
            for(i=startx;i<n-offset;i++){//左开右闭
                mat[i][j]=count++;
            }
            for(j=starty;j<n-offset;j++){
                mat[i][j]=count++;
            }
            for(i=n-offset;i>startx;i--){
                mat[i][j]=count++;
            }
            for(j=n-offset;i>starty;j--){
                mat[i][j]=count++;
            }
            startx++;
            starty++;
        }
        if(n%2==1){
            mat[mid][mid]=count;
        }
        return mat;
    }
};

依旧报错,错误出在写for循环时将i和j搞混,实际上一开始从左到右是遍历的j,我搞成了i,导致结果错误。说明细节十分重要。

再一次修改终于成功:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> mat(n, vector<int>(n, 0));
        int i,j;
        int count=1;
        int loop=n/2;
        int mid=n/2;//n为奇数的时候单独处理一下
        int startx=0,starty=0;//每次开始循环时的起始位置
        int offset;
        for(;loop>0;loop--){
            i=startx;
            j=starty;
            offset=n/2+1-loop;//偏移量,每次循环都要加一
            for(j=starty;j<n-offset;j++){
                mat[i][j]=count++;
            }
            for(i=startx;i<n-offset;i++){//左开右闭
                mat[i][j]=count++;
            }
            for(j=n-offset;j>starty;j--){
                mat[i][j]=count++;
            }
            for(i=n-offset;i>startx;i--){
                mat[i][j]=count++;
            }
            startx++;
            starty++;
        }
        if(n%2==1){
            mat[mid][mid]=count;
        }
        return mat;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值