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

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

有序数组的平方

题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/

文章讲解:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html

视频讲解: https://www.bilibili.com/video/BV1QB4y1D7ep

思路

  1. 暴力解法

    把每一个元素平方,然后排序

  2. 双指针

    平方之后原来的有序数列改变顺序的原因就是负数平方之后可能大于原来的整数,所以只需比较负数平方后是否大于后面的正数,然后插入相应位置即可

    具体实现流程:

    初始:

    • 两个指针left,right分别指向数组的首尾
    • 定义一个同样大小的新数组作为返回结果,并定义下标ans,指向末尾

    移动指针,循环比较:

    • 右指针平方 > 左指针平方时,right 所指的数的平方放入新数组ans下标处,右指针左移,直至找到一个正数平方小于当前left所指的负数平方(此时相当于找到了要将负数平方插入正数群体中的位置)
    • 左指针平方 > 右指针平方时,left 所指的数的平方放入新数组ans下标处,左指针右移,进行下一个负数与正数的比较

    结束条件:

    • 通常在双指针法中,特别是在从左右两端向中间夹逼的场景,循环的终止条件一般都是是 left == right。因为当 leftright 指针相遇时,意味着已经完成了所有需要的填充或操作,没有更多的元素需要处理,本题也不例外。

代码

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;

    //双指针实现-------------------------------
    // 定义左指针和右指针,初始化为数组的首尾元素
    int left = 0;
    int right = nums.size() - 1;
    // 定义结果数组,长度为nums.size(),初始化为0
    int ans = nums.size() - 1 ;
    vector<int> res(ans + 1, 0);
    // 当左指针小于等于右指针时,进行循环
    while(left <= right)
    {
        // 如果左指针指向的元素平方大于右指针指向的元素平方
        if(nums[left] * nums[left] > nums[right] * nums[right])
        {
            // 将左指针指向的元素平方添加到结果数组的对应位置,左指针向右移动
            res[ans--] = nums[left] * nums[left];
            left++;
        }
        // 如果右指针指向的元素平方大于左指针指向的元素平方
        else
        {
            // 将右指针指向的元素平方添加到结果数组的对应位置,右指针向左移动
            res[ans--] = nums[right] * nums[right];
            right--;
        }
    }
    // 返回结果数组
    return res;
}

长度最小的数组

我一开始还以为是这个子数组不需要是连续的,和数学中的数列的子列一样【笑哭】,然后我就直接排序来做了,真是无语了。

题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/

文章讲解:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html

视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE

思路

  1. 暴力解法

    两个for循环不断寻找符合条件的子数组

  2. 滑动窗口法

    核心:不断调整窗口(子序列)的起始与结束位置,直至达到目标

    其实就是用for循环做了两个for循环做的事,但是省去了很多无用操作,前面暴力解法中的第一个for循环就是起始位置,第二个for循环就是结束位置

    具体实现过程

    • 快指针右移:慢指针不冻,快指针右移,sum求和快慢指针之间数字并与target比较大小,直至sum的大小>=target时停止
    • 慢指针右移:当快指针停止时,慢指针逐步右移,逼近最短距离min_len(局部),然后再启动快指针右移,寻找下一个min_len
    • 循环上述过程直至找到全局的min_len

    209.长度最小的子数组

代码

int minSubArrayLen(int target, vector<int> &nums)
{
    // 滑动窗口法
    int slow = 0;
    int fast = 0;
    int sum = 0;
    int min_len = nums.size() + 1;
    while (fast < nums.size())
    {
        sum += nums[fast];
        // 当sum大于等于target时,尝试缩小窗口
        while (sum >= target)
        {
            int len = fast - slow + 1;
            // 更新最小长度
            if (min_len > len)
            {
                min_len = len;
            }
            sum -= nums[slow];
            slow++;
            // 当sum小于target时,缩小到最小长度了,跳出循环
            if (sum < target)
            {
                break;
            }
        }
        fast++;
    }
    // 如果min_len等于nums.size() + 1,说明没有找到符合条件的子数组,返回0
    return min_len == nums.size() + 1? 0 : min_len;
}

螺旋矩阵

题目链接:https://leetcode.cn/problems/spiral-matrix-ii/
文章讲解:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html
视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/

自己是第二次做这个题了,第一次是在洛谷上做的,第一次整半天都不会,第二次好多了

思路

考察的是模拟。我这次对这题的第一想法是递归(其实是因为写递推怕写错,算法菜狗落泪),因为每一轮顺时针转圈都是同样的操作,并且下一轮的转圈其实只需看成在一个新的被裁剪掉最外圈的矩阵上的最外圈转(有点绕)。

递归实现

每次矩阵都从start处开始顺时针转圈,并且每条边都只走size-1个格子,留下一个给下一个开头

  • 上边

    矩阵行下标固定为start,从矩阵列下标为start处开始向右走到start+size-2 (start+size-1刚好为最后一列/行),留下start+size-1当做右边的开头

  • 右边

    矩阵列下标固定为start + size -1,行下标从start向下走到start+size-2

  • 下边

    同理,行固定为start+size-1,列从start+size-1到start-1

  • 左边

    行从start+size-1到start-1,固定为start

  • 递归

    start+1进入下一个矩阵,size-2(缩小一圈,减小两行/列)

结束条件

​ 1.n为奇数,则最后会缩到一个size=1的单元格,此时赋上值后结束

​ 2.n为偶数,则最后转完最后一圈后size=0,直接结束

当然理解了以上内容可以发现递归完全没有必要,直接for循环递推就行

在这里插入图片描述

代码

void fill(vector<vector<int>> &matrix, int start, int size, int num)
{
    //终止条件
    if(size < 0) return;
    if(size == 1) matrix[start][start] = num;

    //start + size - 1 表示最后一行/列的下标
    //每行填充 size - 1个数,最后一个数留给下一条边的开头 
    //上边
    for(int i = 0; i < size - 1; i++)
    {
        matrix[start][start + i] = num;
        num++;
    }

    //右边
    for(int i = 0; i < size - 1; i++)
    {
        matrix[start + i][start + size - 1] = num;
        num++;
    }

    //下边
    for(int i = 0; i < size - 1; i++)
    {
        matrix[start + size - 1][start + size - 1 - i] = num;
        num++;
    }

    //左边
    for(int i = 0; i < size - 1; i++)
    {
        matrix[start + size - 1 - i][start] = num;
        num++;
    }

    //递归填充
    fill(matrix, start + 1, size - 2, num);
}
vector<vector<int>> generateMatrix(int n) {
//螺旋矩阵
    vector<vector<int>> matrix(n, vector<int>(n));
    fill(matrix, 0, n, 1);
    return matrix;
}

for循环递推版本

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int t = 0;      // top
        int b = n-1;    // bottom
        int l = 0;      // left
        int r = n-1;    // right
        vector<vector<int>> ans(n,vector<int>(n));
        int k=1;
        while(k<=n*n){
            for(int i=l;i<=r;++i,++k) ans[t][i] = k;
            ++t;
            for(int i=t;i<=b;++i,++k) ans[i][r] = k;
            --r;
            for(int i=r;i>=l;--i,++k) ans[b][i] = k;
            --b;
            for(int i=b;i>=t;--i,++k) ans[i][l] = k;
            ++l;
        }
        return ans;
    }
};

数组总结

二分法

  1. 注意边界条件
  2. 两种写法:左闭右闭,左闭右开

双指针法

  1. 用快慢指针在一个for循环干了两个for循环的活
  2. 双指针就好像你的两根手指头指着数组的不同地方,可以同时同空间进行两件事情

滑动窗口

  1. 类似于剪辑中的截取片段,不断调整窗口的起始位置和末尾位置来达到目标
  2. 一般是先移动end,再移动start

模拟

严格抓住循环不变量统一的区间定义,转移方式),例如螺旋矩阵中的左闭右开原则(只走size-1个格子)就是在一轮大循环中的不变量,每一条边都遵循此原则那么逻辑非常清晰

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的数组,使得数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值