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

977. 有序数组的平方

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

文章讲解:代码随想录—977.有序数组的平方

第一思路:刚看到本体的第一想法就是文章中的暴力算法,先对数组进行平方运算,再对平方后的数组排序

暴力算法

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

时间复杂度:O(n + nlog n)

空间复杂度:O(1)

双指针算法

思路:

原数组是按递增顺序排列的,在数组平方运算后能让原本递增顺序改变的,只可能是因为数组中存在负数。

如果数组既存在正数、又存在负数,那数组左半部分(负数部分)经平方运算后会呈递减排列,右半部分(正数部分)会呈递增排列。也就是说,平方后数组的最大值只会出现在数组两端,并向中间递减。

如果只有正数,数组右端存在最大值;如果只有负数,数组左端存在最大值。综上,满足平方后数组的最大值只会出现在数组两端这一条件。

因此,本题解决方案就是运用双指针从两边出发对数值进行比较,将比较后的更大值依次更新到新建数组中。

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

时间复杂度:O(n)

空间复杂度:O(1)

209. 长度最小的子数组

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

文章讲解:代码随想录—209. 长度最小的子数组

第一思路:接触过滑动窗口的解法,可以想到用该法解题

暴力算法

简单粗暴两层 for 循环,时间复杂度可想而知 O(n ^ 2)

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
            int res = INT32_MAX;
            int sum = 0;
            int len = 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) {
                        len = j - i + 1;
                        res = res < len ? res : len;
                        break;
                    }
                }
            }
            return res == INT32_MAX ? 0 : res;
    }
};

时间复杂度:O(n ^ 2)

空间复杂度:O(1)

滑动窗口算法

思路:

很容易想到,本题中需要不断更改子序列的起始位置和终止位置,就像一个窗口在数列中滑动。

初始先从第一个元素开始,向子序列中添加元素,直到子序列中的元素和 >= target,记录子序列长度。

第一个满足条件的窗口成立后,起始位置不变的情况下,后面继续添加元素只会在满足条件的情况下增加窗口长度,因此应更改窗口起始位置后再滑动窗口,直到找到符合条件的最小长度值。

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

时间复杂度:O(n)

空间复杂度:O(1)

59. 螺旋矩阵 II

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

文章讲解:代码随想录—59. 螺旋矩阵 II

思路:

找好边界,左闭右开,按着数字填充顺序填写

记号循环圈数,如果矩阵边长是奇数,需要单独填充中间值

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
            vector<vector<int>> res(n, vector<int>(n, 0));
            int x = 0, y = 0;         //记录每一圈的起始位置
            int count = 1;            //标记数值
            int offset = 1;           //每一行或每一列行走的终点偏移量
            int loop = n / 2;         //记录圈数
            int mid = n / 2;          //记录中间位置
            
            while (loop --) {
                int i = x, j = y;

                for ( ; j < n - offset; j ++) res[i][j] = count ++;

                for ( ; i < n - offset; i ++) res[i][j] = count ++;

                for ( ; j > y; j --) res[i][j] = count ++;

                for ( ; i > x; i --) res[i][j] = count ++;

                x ++;
                y ++;                 //更新下一圈起始位置

                offset ++;
            }

            if (n % 2) res[mid][mid] = count;

            return res;
    }
};

时间复杂度:O(n ^ 2)

空间复杂度:O(1)

总结

二分法:目前看来使用条件是有序数组,没有重复元素时二分法更简单,时间复杂度一般为O(log n)。重点是要把握好边界!

双指针:目的是能在一个循环内完成两个循环的工作,时间复杂度为O(n)。目前练习的题目中包含了快慢双指针和左右双指针,灵活应对。

滑动窗口:涉及到子序列在数组中滑动以满足条件,时间复杂度为O(n)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值