2022年10月13日学习打卡

91 阅读4分钟

day2

算法题

977.有序数组的平方

题目建议: 本题关键在于理解双指针思想

题目链接:leetcode.cn/problems/sq…

文章讲解:programmercarl.com/0977.%E6%9C…

视频讲解: www.bilibili.com/video/BV1QB…

个人思路: 最简单的方法肯定是暴力法,即将每个数原地平方,然后进行排序,由于排序的时间复杂度最小为O(nlogn),时间复杂度较高,如果希望达到o(n)的复杂度,可发现中间的元素是最小的,距离0的距离最短,并且可以整个数组的元素分为两种,一种为正数,一种为负数,并且由于本身数组有序,因此负数从中间向左开始绝对值开始变大,而正数则从中间向右开始绝对值开始变大,对两组元素进行归并排序操作,由于本身有序,因此只需要执行归并操作的最后一步,时间复杂度为o(n),同时注意两种特殊情况,即全正和全负的场景。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {

        int n = nums.size();
        //printf("%d\n",n);
        int start = 0;int end = nums.size() -1;
        // int mid = (start + (end - start))/2;
        int mid =0;
        int find_0 = -1;
        for(mid=0;mid<n;mid++)
        {
            if(nums[mid]>=0) {
                find_0 = mid;
                break; 
            }
        }
        if(find_0 == -1) mid = n;
        else mid = find_0;
        vector<int> newnums(n);
        if(mid == n){   //全负数
            for(int j=n-1,k=0;j>=0;j--,k++)
            {
                newnums[k] = nums[j] * nums[j];
            }
            return newnums;
        }
        else if(mid ==0){           //全正数或者只有一个数
            for(int j=0;j<n;j++)
            {
                newnums[j] = nums[j] * nums[j];
            }
            return newnums;
        }
        else{
            int j=0,k=0,l=0;
            for(j=mid -1,k=mid,l=0;j>=0&&k<n;l++)
            {
                if(nums[j]*nums[j] <nums[k]*nums[k])
                {
                    newnums[l] = nums[j]*nums[j];
                    j--;
                }
                else{
                    //printf("%d %d\n",k, l);
                    newnums[l] = nums[k]*nums[k];
                    k++;
                    
                }
            }
            if(j>=0) while(j>=0) {newnums[l] = nums[j]*nums[j];l++;j--;};
            if(k<=n-1) while(k<n) {newnums[l] = nums[k]*nums[k];l++;k++;};
            return newnums;
        }
        return newnums;
    }
};

看解析后的思路: 可以发现上述代码量偏大,其问题主要在于将数组分为正数和负数,以及两种特殊情况,以及最后当一组数组分配结束后的处理,**同样的思路换一个方向,**由于中间是最小的,那边两边的就一定有一个是最大的,如果从大的数字开始比较,逆向输入到目标数组中,则代码量会大大减少,同时减小边界的判断,bug也会变得更少。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        int l=0,r=n-1;
        vector<int> A(n);
        int i =n-1;
        while(l<=r){
            if(nums[r]*nums[r]>=nums[l]*nums[l]){
                A[i] =nums[r]*nums[r];
                r--;
                i--;
            }
            else{
                A[i] =nums[l]*nums[l];
                l++;
                i--;
            }
        }
        return A;
    }
};

209.长度最小的子数组

题目建议: 本题关键在于理解滑动窗口,这个滑动窗口看文字讲解 还挺难理解的,建议大家先看视频讲解。 拓展题目可以先不做。

题目链接:leetcode.cn/problems/mi…

文章讲解programmercarl.com/0209.%E9%95…

视频讲解:www.bilibili.com/video/BV1tZ…

个人思路: 本题为很明显的滑动窗口,通过控制左边界和右边界,来保证窗口内部的值符合题目的要求,同时在该基础上不断调整边界使得窗口的大小最小,思路较为简单,但由于最开始对于边界的判断过于混乱,导致debug了很长时间。**整理后的思路:**当窗口内的元素之和大于或者等于目标时,就需要调整左边界,从而减小窗口内元素的和,而当窗口内的元素之和小于目标时,则需要调整右边界,从而增加窗口内元素的大小。

代码1:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int min = nums.size()+1;
        int n = nums.size();
        int l =0,r=0;
        int sum = nums[0];   //当数组的长度为1时,没法进行判断
        if(sum>=target) min = 1;
        int flag = 0;
        while(flag == 0){
            if(sum<target)
            {
                if(r<n-1) //边界为 r = n-2
                {
                    r++;
                    sum+=nums[r];
                }
                else{    //边界为 r = n-1 即r已经到达了边界 
                    r++;
                    flag =1;
                }
            }
            else if(sum>=target)
            {
                if(l<n-1) //边界为 l = n-2
                {
                    sum-=nums[l];
                    l++;
                }
                else{    //此时边界为 l = n-1 即l也到达了边界 同时由于sum>=target 此时长度为1
                    flag =1;
                }
                
            }
            printf("sum=%d l=%d r=%d ",sum,l,r);
            if(sum>=target&&r-l+1<min)
            {
                min = r-l+1;
            }
            printf("min=%d\n",min);
        }
        if(min==n+1) return 0;
        return min;
    }
};

**代码2:(代码随想录上面的写法本质上是一样的 ** 代码精简且边界条件更少 值得学习)

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int result = INT32_MAX;
        int sum =0;
        int i=0;
        for(int j=0;j<nums.size();j++)  //用于控制右边界增长
        {
            sum+=nums[j];
            while(sum>=target){   //符合条件
                int len = j-i+1;
                result = len<result?len:result;
                sum-=nums[i];   
                i++;     //左边界变小
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};

59.螺旋矩阵II

题目建议: 本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。

题目链接:leetcode.cn/problems/sp…

文章讲解:programmercarl.com/0059.%E8%9E…

视频讲解:www.bilibili.com/video/BV1SL…

个人思路: 模拟转圈,主要是对边界情况进行判断

出现的问题:

​ 1.对于switch语句不够熟悉,break的用法

​ 2.对于二维容器创建的初始化

​ 3.分清方向,第一维坐标控制上下,第二维控制左右 例如向左为{0,-1}而不是{-1,0}

代码:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> wheel= {{0,1},{1,0},{0,-1},{-1,0}};  //右,下,左,上   注意别写反了
        //vector<vector<int>> table(size1, vector<int>(size2, 0)); 二维容器的初始化
        vector<vector<int>> newMatrix(n,vector<int>(n,0));
        int mi = 0,mj = 0;
        int mw = 0;
        int limr = n-1; //右边界
        int liml = 0; //左边界
        int limdown = n-1; //下边界
        int limup = 0; //上边界
        for(int i=1;i<=n*n;i++)
        {
            newMatrix[mi][mj] = i;
             //printf("mi=%d mj=%d\n",mi,mj);    
            switch(mw) {
                case 0:
                    {
                        if(mj+wheel[mw][1]>limr) {mw+=1;mw%=4;limup+=1;} //向右走到尽头,切换方向,同时上边界+1,即第二维坐标
                        break;
                    }
                case 1:
                    {
                        //printf("mw = %d -----%d \n",mw,wheel[mw][0]);
                        if(mi+wheel[mw][0]>limdown) {mw+=1;mw%=4;limr-=1;} //向下走到尽头,切换方向,同时右边界-1,即第一维坐标
                        break;
                    }
                case 2:
                    {
                        if(mj+wheel[mw][1]<liml) {mw+=1;mw%=4;limdown-=1;} //向左走到尽头,切换方向,同时下边界-1,即第二维坐标
                        break;
                    }
                case 3:
                    {
                        if(mi+wheel[mw][0]<limup) {mw+=1;mw%=4;liml+=1;} //向上走到尽头,切换方向,同时左边界+1,即第一维坐标
                        break;
                    }
                default:
                    break;
            }
            mi += wheel[mw][0];
            mj += wheel[mw][1];
                  
        }
        return newMatrix;
    }
};

题解思路: 该题不涉及太多的算法问题,主要是对整个逻辑进行模拟,考察代码能力。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
        int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
        int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
        int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
        int count = 1; // 用来给矩阵中每一个空格赋值
        int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
        int i,j;
        while (loop --) {
            i = startx;
            j = starty;

            // 下面开始的四个for就是模拟转了一圈
            // 模拟填充上行从左到右(左闭右开)
            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++;
            }

            // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
            startx++;
            starty++;

            // offset 控制每一圈里每一条边遍历的长度
            offset += 1;
        }

        // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
        if (n % 2) {
            res[mid][mid] = count;
        }
        return res;
    }
};