首页 > 编程语言 >代码随想录算法训练营Day02|977.有序数组的平方、209.长度最小的子数组、59. 螺旋矩阵 II

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

时间:2022-11-17 21:48:23浏览次数:67  
标签:977 nums int res 随想录 vector 数组 offset

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

977.有序数组的平方

题目链接:977.有序数组的平方

首先还是仔细审题,题目给出的数组为非递减顺序,平方生成的新数组也必须是非递减顺序


题解如下:

①暴力解法

最简单必然是暴力解法,我们直接对数组进行原地修改,然后进行快速排序。

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;
    }
};

但该解法的时间复杂度为O(n+nlogn),所以遇到大规模的输入会出现超时的情况。

快速排序的时间复杂度为O(nlogn)

②双指针法

主要思想是数组本身有序,所以平方后最大值只可能出现在数组的两端。于是我们同时判断左右两端平方后的数组大小,将数值较大的一边倒序插入返回的结果数组。

关于数组两端平方相等的情况,随便插入其中一端即满足非递减的数组要求。

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

时间复杂度提升为O(n)。

209.长度最小的子数组

题目链接:209.长度最小的子数组

首先需要注意题干:

  • 要求的是连续子数组:所以元素累加时中间不能断
  • 给定数组并未强调排序:在设计滑动窗口时需要注意

①暴力解法

将每个下标依次作为连续子数组的起点进行遍历,找到满足条件的最小子数组长度。

注意最小数组长度的初始值设置为INT_MAX

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

暴力法因为算法复杂度为O(n*n),遇到大规模的数据集会报错。

②滑动窗口法

滑动窗口的大致思想是在原数组上移动前后端点的同时,将累加和与目前值target进行比对,如果超出范围则将起点的坐标向前移动。我们需要注意目前长度需要初始化为INT_MAX,因为滑动过程中可能出现多个满足target的情况,而我们只需要取最小值即可,代码如下:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // 注意子数组长度的初始值
        int res = INT_MAX;
        // 滑动窗口初始点不定时变化,所以单拎出来
        int subLen = 0, i = 0, sum = 0;
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            while (sum >= target) {
                subLen = j - i + 1;
                res = res < subLen ? res : subLen;
                sum -= nums[i++];
            }
        }
        return res == INT_MAX ? 0 : res;
    }
};

虽然代码形式为for循环与while循环的嵌套,但实际上实际复杂度为O(n)。因为从代码逻辑上考虑每个元素都在for循环中进入一次窗口,而在while循环中移出一次窗口,虽然会存在一个for循环内起点坐标多次移动的情况(指的是while循环),但整体来说所有元素都是一进一出。所以时间复杂度严格来说是O(n*2)。

59. 螺旋矩阵 II(⭐️高频)

题目链接:59. 螺旋矩阵 II

不用想复杂了,这题重在代码逻辑而不是算法。

题干说明了数字的循环逻辑是:

  • 从左往右
  • 从上往下
  • 从右往左
  • 从下往上

而且每经历一圈,起始点的范围都会向内进行收缩。关键在于保证循环不变量统一,也就是四个for循环的边界均为左开右闭。

截屏2022-11-17 21.30.00

另外需要考虑奇偶边长的不同情况:虽然它们的循环次数都为n/2,但奇数边长的中心点需要单独拎出来进行赋值

代码如下:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
    		// 先讨论n=1的特殊情况,此时不需要进行循环
    		if (n == 1) {
            vector<vector<int>> res(n, vector<int>(n, 1));
            return res;
        }
        vector<vector<int>> res(n, vector<int>(n, 0));
        
        int num = 1;
        int offset = 0;
        int loop = n / 2;
        while (offset != loop) {
            int i, j;
            for (i = 0 + offset, j = 0 + offset; j < n - 1 - offset; j++) {
                res[i][j] = num++;
            }
            for (i = 0 + offset, j = n - 1 - offset; i < n - 1 - offset; i++) {
                res[i][j] = num++;
            } 
            for (i = n - 1 - offset, j = n - 1 - offset; j > 0 + offset; j--) {
                res[i][j] = num++;
            }
            for (i = n - 1 - offset, j = 0 + offset; i > 0 + offset; i--) {
                res[i][j] = num++;
            }
            offset++;
        }
        // 单独给奇数边长的中心点赋值
        if (n % 2)
            res[n/2][n/2] = num;
        return res;
    }
};

标签:977,nums,int,res,随想录,vector,数组,offset
From: https://www.cnblogs.com/buryinshadow/p/16901070.html

相关文章

  • 代码随想录Day26
    LeetCode513.找树左下角的值      思路:思路1:需要遍历所有路径,找出深度最大的一条路径,并且是左叶子结点的值。思路2:层序遍历最左值。 递归遍历写法:前序......
  • LeetCode刷题(4)【移除元素&合并两个有序数组】(C语言)(含图解)
    移除元素典型双指针玩法。27.移除元素-力扣(LeetCode)(leetcode-cn.com)我们都会想到这样的解法:从前面依次往后推,是val就将该数据后面的元素依次覆盖上来,但是这样的时间复......
  • javascript js 对象数组 转json 数组解构
           ......
  • 04-Java-数组
    Java-数组一个类型所有数据的有序集合每个数据称作一个数组元素,通过每个组的元素可以用下标访问它数组是声明与创建首先必须声明数组变量,才能在程序种使用数组。d......
  • 数组指针和指针数组?
    首先,理解一下数组指针和指针数组这两个名词:“数组指针”和“指针数组”,只要在名词中间加上“的”字,就知道中心了——数组的指针:是一个指针,什么样的指针呢?指向数组的指针......
  • Day14.3:数组的三种初始化理解
    数组的三种初始化静态初始化即数组的声明和赋值一起完成int[]arrays={1,2,3,4,5};动态初始化-——手动赋值(包含默认初始化)声明数组的但不赋以确切的值,没有赋值......
  • shell 两个数组比较,得到元素的并集、交集等
    linuxshell实现数组比较,取元素的并集、交集时,可以使用sort排序、uniq统计和awk数据过滤。shell实现如下file_list_1=("test1""test2""test3""test4""test5""tes......
  • js判断对象是否为数组的方法
    1.使用Array.isArray()方法,推荐letarr=[1,2,3,4]console.log(Array.isArray(arr))//true 2.使用Object.prototype.toString.call()方法,该方法不仅能判断......
  • 利用数组构建二叉树(随笔)
    做leetcode的时候,看到示例,突然想自己构建一颗树。。随即自己写了尝试写了一个方法(比较随意)测试用例://example-1[2,1,3]//example-2[2,null,3]//example-3[5,3......
  • 35:列表_元素删除的三种方式_删除本质是数组元素拷贝
    ###列表元素的删除###del删除删除列表指定位置的元素。>>>a=[100,200,888,300,400]>>>dela[1]>>>a[100,200,300,400]###pop()方法pop()删除并返回指定位置......