代码随想录训练营第二天|LeetCode977 有序数组的平方、LeetCode209 长度最小的子数组、LeetCode59 螺旋矩阵

LeetCode 977.有序数组的平方

题目连接

视频连接

文章连接

第一眼看到这道题的时候,想到的是暴力解法,就是先把所有数据先进行平方,然后再把数组进行排列。

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

但是这样子就算用快排,在时间复杂度的角度来说,也是O(n+logn)不能达到极简,而且题目中也要求时间复杂度为O(n)的算法,也就是我们只能遍历一遍数组。

而且题目中明确告诉我们数组是非递减排列的,也就是说,数组里的数经过平方后,会出现两端大,中间小的情况。我们不妨用上上一天所用过的双指针思路,但这个双指针不是一快一慢的关系,而是分布在数组两端,然后一起向中间移动。即首先比较当前位置的一头一尾指针哪个绝对值更大,将更大的数放到新的数组里面进行保存。但要注意,我们得到的是数最大的,要想在新数组中从小到大排列,我们就需要从后往前去放数据。

代码如下:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size() - 1;
        vector<int> res(n + 1, 0);
        
        int Head = 0, Tail = n;

        while (Head != Tail)
        {
            if (abs(nums[Head]) > abs(nums[Tail]))
            {
                res[n--] = nums[Head] * nums[Head++];
            }
            else
            {
                res[n--] = nums[Tail] * nums[Tail--];
            }
        }
        
        res[n] = nums[Head] * nums[Head]; //记得最后补上两个指针相遇时的数
        return res;
    }
};

我们发现现在的情况就是时间复杂度为O(n),就省去了后面的排序。但是也会发现我们开辟了一个新的数组,也就是进行了拿空间换时间的操作。

LeetCode 209.度最小的子数组

题目连接

视频连接

文章连接

看到题目的一瞬间我是懵的,因为这是我第一次看到这样的题目,首先是不懂题目的意思,子数组的概念没搞明白,然后就是不知道怎么去找。

后来查了之后才知道,子数组的定义:一个或连续多个数组中的元素组成一个子数组(子数组最少包含一个元素)。这样子就明白了很多,它跟子序列不同,子序列的定义:子序列就是在原来序列中找出一部分组成的序列(子序列不一定连续。所以我们首先可以用暴力解法去破解它。

暴力解法:使用两个for循环,外层for循环是起始位置,逐渐向数组尾端移动,另一组for循环是终止位置,由不同的起始位置出发,一步一步向末尾位置移动,途中记录满足条件的最小长度。这样子的时间复杂度为O(n²)。

代码如下:

class Solution {
public:
    int minSubArrayLen(int s, vector<int> &nums)
    {
        int result = INT32_MAX; // 最终的结果
        int sum = 0; // 子数组的数值之和
        int subLength = 0; // 子数组的长度
        for (int i = 0; i < nums.size(); i++)
        { // 设置子数组起点为i
            sum = 0;
            for (int j = i; j < nums.size(); j++)
            { // 设置子数组终止位置为j
                sum += nums[j];
                if (sum >= s)
                { // 一旦发现子数组和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;//拿这一次的跟之前的作比较
                    break; // 因为我们是找符合条件最短的子数组,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子数组
        return result == INT32_MAX ? 0 : result;
    }
};

然后就是用双指针,这次的双指针的作用是做一个滑动窗口。用一个for循环去遍历,这里for循环里面遍历的是终止位置(即Tail),从0开始,一直到最后一个元素。我们在for循环里面去把每一个Tail所遍历的元素都相加起来得到sum,直到sum大于等于目标值,记录这时候的长度。然后让起始位置(Head)开始一点一点往后移动,每移动一次更新一次最小长度值Res,直到sum小于目标值,这时候重回for循环里面,继续移动Tail。

窗口的起始位置(Head)如何移动:如果当前窗口的值大于sum了,窗口就要向前移动了(也就是该缩小了)。

窗口的终止位置(Tail)如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

代码如下:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int N = nums.size();
        int Head = 0, Tail = 0;
        int Sum = 0;
        int Res = INT32_MAX; //初始化的时候,让Res为一个32位整型最大值,方便后面找出更小的
        for (; Tail < N; Tail++)
        {
            Sum += nums[Tail];
            while (Sum >= target)
            {
                Res = min(Res, Tail - Head + 1);
                Sum -= nums[Head++];
            }
        }
        return Res == INT32_MAX ? 0 : Res; 
    }
};

LeetCode 59.螺旋矩阵

题目连接

视频连接

文章连接

这道题看起来并不麻烦,不涉及算法的设计,主要注重一个过程的模拟。四条边的循环,应该设置什么条件?怎么去设置?奇偶层的变化数组中心位置怎么处理?这些才是这道题应该注意的。

就像当初写二分法的时候一样,我们都要追寻一个循环不变量,制定一个统一的规则,让我们在写代码或者理解的时候能够更加容易分辨界限,不会陷入到循环的漩涡中,也不会写出每条边不一样的判定条件。

在这里我们设定左闭右开原则,即同一条边的最后一个元素(即拐角)留给下一条边作为起点,这样子就能避免重复、遗漏的问题。

正确代码如下:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n)
    {
        vector<vector<int>> Res(n, vector<int>(n, 0));
        int startX = 0, startY = 0;
        int offset = 1, count = 1;
        int Round = n / 2;
        while (Round--)
        {
            int x = startX;
            int y = startY;
            for (; x < n - offset; x++)
            {
                Res[y][x] = count++;	// 注意数组x,y的位置,是Res[y][x],并不是Res[x][y]
            }
            for (; y < n - offset; y++)
            {
                Res[y][x] = count++;
            }
            for (; x > startX; x--)
            {
                Res[y][x] = count++;
            }
            for (; y > startY; y--)
            {
                Res[y][x] = count++;
            }

            startX++;
            startY++;
            offset++;
        }
        if (n % 2 == 1)//如果使奇数,中心位置补充一个
        {
            Res[startY][startX] = count;
        }
        return Res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值