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

代码随想录算法训练营

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


前言

代码随想录算法训练营第二天
主要内容是双指针,滑动窗口,模拟


一、LeetCode977.有序数组的平方

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

1 思路

(1)特殊之处

1)nums是升序的
2)结果要求是升序的
3)在平方时,以0为分界,结果是先增后减的

(2)做法解析:双指针是要点

1)先考虑平方以后怎样获得升序数组
既然平方的结果先増后减,那么我们考虑使用归并的思想,用两个指针对左右两侧(正负两侧)进行遍历,比较大小。
在这里用i遍历正数和0的部分,用j遍历负数部分

nums[i]*nums[i]> nums[j]*nums[j]nums[i]*nums[i]<= nums[j]*nums[j]
选择 j 所代表的平方结果,j 移动, i不动选择 i 所代表的平方结果,i 移动,j不动

2)接下来我们分析指针具体怎样移动
首先找出正负的分界:让i先移动,从左向右遍历,遇到第一个非负的数,break跳出循环
接着找到j的起点:此时i指向第一个非负数,也就是平方值最小的非负数,j=i-1,使得j从平方值最小的负数开始,i和j从中间向两边遍历。i递增,j递减。

(3)细节

1)j何时赋值

2)循环的边界
在最开始我用i < n || j >= 0作为边界,在跳出循环后单独处理没有到头的那一端
然而循环条件为i < n || j >= 0更好:
把对某一方遍历完而另外一边没有完成的情况放在循环里,而不是单独讨论,单独讨论可能缺项。
这样的做法可以不必单独处理只有正或者只有负的情况,因为这两种是极端的正/负一方提前用完,而另外一方还有剩余的情况
3)何时平方
我的做法与官方题解在平方的处理上不同。
官方的每次比较都会进行一次平方运算,最终压入结果vector时还要再算一次平方。
我的做法中,在遍历寻找正负分界的过程(for循环)中进行平方并存放在nums中,这样可以得到所有负数和第一个非负数的平方。在双指针比较(while循环)时,每次更新i,都计算nums[i]的平方并赋值给nums[i]。
这样比较和结果赋值都用nums即可。

2 题解

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int i = 0;
        int j = 0;
        int n = nums.size();
        vector<int> a;
        for (i = 0; i < n; i++) {

            if (nums[i] >= 0) {
                nums[i] *= nums[i];

                break;
            }
            nums[i] *= nums[i];
        }
        j = i - 1;
        while (i < n || j >= 0) {
            if (i == n) {
                a.push_back(nums[j]);
                j--;
            } else if (j < 0) {
                a.push_back(nums[i]);
                i++;
                if (i < n)
                    nums[i] *= nums[i];
            } else if (nums[j] < nums[i]) {
                a.push_back(nums[j]);
                j--;
            } else {
                a.push_back(nums[i]);
                i++;
                if (i < n)
                    nums[i] *= nums[i];
            }
        }
        return a;
    }
};

二、LeetCode209.长度最小的子数组

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

1.滑动窗口法

滑动窗口是双指针的一种。
(1)指针i,j分别表示子数组的终点和起点,先移动i,发现子数组长度大于target后再左移j
(2)每次只要子数组长度大于target就保存答案
(3)子数组长度小于target后,向左移动i

2.细节

何时进行sum+=nums[i]:在while循环之前,如果在循环之后,那么i=n-1时,求和后i变为n,跳出for循环,漏掉i=n-1的情况
如何保存答案:每次只要子数组长度大于target,就要将现在的子数组长度和现有的答案进行比较,保存较小的
len的初值:由于更新方式是每次取小的值作为结果,那么len初值需要大于任何一个可能的子数组长度。子数组长度最大为n,所以len初值大于n

3.题解

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int i = 0; // 子数组终点
        int j = 0; // 子数组起点
        int n = nums.size();
        int len = n + 1;
        int sum = 0;
        for (i = 0; i < n; i++) {
            sum += nums[i];
            while (sum >= target) {
                len = min(len, i - j + 1);
                sum -= nums[j];
                j++;
            }
        }
        if (len > n)
            return 0;
        else
            return len;
    }
};

三、LeetCode59.螺旋矩阵II

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

1.方法

这道题主要进行模拟,用到了前面二分法对区间的定义
模拟时注意同统一区间

2.思路

(1)将整个过程分成一圈一圈的,总圈数n/2
(2)将一圈分为四条边,每条边左开右闭,如表所示

(3)实现每一条边
1)第i圈,每条边长度为n-(2*i-1)
2)每条边的填充方式
上:y++
右:x++
下:y–
左:x–
3)填充的数由不断增加的count实现

3.题解

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> ans(n, vector<int>(n, 0));
        ans[0][0] = 1;
        int startx = 0;
        int starty = 0;
        int x = 0;
        int y = 0;
        int loop = n / 2; // 一共多少圈
        int len;          // 每条边的长度都相等 len=n-(2*i-1)(第i圈)
        int count = 1;
        for (int i = 1; i <= loop; i++) {
            x = startx;
            y = starty;
            len = n - (2 * i - 1);
            for (int j = 0; j < len; j++, y++) {

                ans[x][y] = count;
                count++;
            }
            for (int j = 0; j < len; j++, x++) {

                ans[x][y] = count;
                count++;
            }
            for (int j = 0; j < len; j++, y--) {

                ans[x][y] = count;
                count++;
            }
            for (int j = 0; j < len; j++, x--) {

                ans[x][y] = count;
                count++;
            }
            startx++;
            starty++;
        }
        if (n % 2)
            ans[n / 2][n / 2] = count;
        return ans;
    }
};

总结

数组的内容到这里告一段落
数组的主要内容有
1、二分法
(1)两种不同的二分法写法
(2)二分思想的应用:处理单调递增或单调递减的问题,例如求满足条件的最小结果
2、双指针:
两个指针各自负责一部分
通过一个快指针和慢指针在一个for循环下完成两个for循环

3、滑动窗口
先移动子数组的终点指针,发现子数组和>=target以后移动子数组起点指针,每移动一次起点指针,都要更新一次结果,直到子数组和<target,再更新重点指针
4、模拟
(1)模拟需要确定循环不变量,在整个过程中用统一的区间定义,统一的转移方式
(2)模拟需要拆解问题,问题中相似的过程归为一类,先分析类似问题中的一个,用循环实现

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值