Day2|977.有序数组的平方/209.长度最小的子数组/59.螺旋矩阵II

977.有序数组的平方 

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

  • 输入:nums = [-4,-1,0,3,10]
  • 输出:[0,1,9,16,100]
  • 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]

示例 2:

  • 输入:nums = [-7,-3,2,3,11]
  • 输出:[4,9,9,49,121]

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解:代码随想录

视频讲解: 双指针法经典题目 | LeetCode:977.有序数组的平方_哔哩哔哩_bilibili

刚开始做这道题的时候没有思路,没法想出来这几个数字变化前后的关系,脑海里的想法是:通过绝对值函数去比较大小,然后再通过指针进行排序。想了一会还是决定去看题解。

思路:

1. 暴力排序:每个数平方之后,排个序

2. 双指针法:数组其实是有序的, 只不过负数平方之后可能成为最大数了。那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。

于是我决定通过这两个思路去构建代码来解决,暴力排序理解思路后其实一下就水到渠成了

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

 然后接着去构思双指针法, 最终代码如下:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int leftindex = 0;                // 定义一个从左边开始遍历的指针
        int rightindex = nums.size() - 1; // 定义一个从右边开始遍历的指针
        int index = nums.size() - 1;      // 定义一个插入指针
        vector<int> result(nums.size());  // 定义结果数组
        while(leftindex <= rightindex && index >= 0){
            // 右边平方大的数存入
            if( nums[leftindex]*nums[leftindex] <= nums[rightindex]*nums[rightindex]){
                result[index] = nums[rightindex]*nums[rightindex];
                index --;
                rightindex --;
            }
            // 左边平方大的数存入
            else{
                result[index] = nums[leftindex]*nums[leftindex];
                index --;
                leftindex ++;
            }
        }
        return result;
    }
};

对比着Carl的代码, 我的定义内容显得更加繁琐, while语句中的两个条件中, index >= 0 在我通过画图理解之后发现可以删除. 而且可以通过一个for语句来定义左指针和限定范围, 这算是一点值得改进的地方, 另外我的画图理解:

这道题要注意理解双指针的改变条件以及结束遍历的条件是左指针大于右指针的时候.

209.长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

示例:

  • 输入:s = 7, nums = [2,3,1,2,4,3]
  • 输出:2
  • 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

提示:

  • 1 <= target <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

 

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解:代码随想录

视频讲解:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili

思路: ..... 没有思路, 果断去看文章讲解, 搭配滑动窗口动态演示图, 终于略微有了一些理解 

 

 引用leetcode本题题解区的一个理解:

可以理解为左右指针中间窗口的sum为两指针的“共同财产”,就是右指针一直在努力工作挣钱,好不容易共同财产大过target,记录一下两指针之间的距离,结果左指针就开始得瑟挥霍,不停花钱(往右移动),结果花钱一直花到sum又小过target,此时右指针不得不再次出来工作,不停向右移动,周而复始,最后取左右指针离得最近的时候

 其实也跟双指针有很大的关系, "最后取左右指针离得最近的时候", 这里的条件是: 直到右指针无法再挣到钱(即 右指针 == nums.size()的时候), 感觉解释的已经非常清楚了.

这道题最重要的部分就是滑动窗口处的代码, 尤其是当滑动窗口内的值已经大于目标值的时候:

// sum是滑动窗口内元素的和
// target是目标整数
// subLength是滑动窗口元素的个数, 也就是滑动窗口的长度
// left是左指针, right是右指针
// result是最终长度输出, 这里给result赋值一个很大的数

while(sum >= target){
    subLength = right - left + 1; // 计算子序列的长度
    result = result < subLength ? result : subLength;  
    //如果存在子序列满足和大于目标整数,将subLength的值赋值给result, 否则不存在满足条件的子序列. 
    left++;
}

最终代码如下:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum = 0;         // 定义滑动窗口的总和
        int result = INT32_MAX;
        int leftindex = 0;   // 定义滑动窗口左侧的值
        int subLength = 0;   // 定义滑动窗口子序列的个数
        for(int rightindex = 0; rightindex < nums.size(); rightindex++){
            sum += nums[rightindex];
            while( sum >= target ){ // 滑动窗口中的数大于目标值时
                subLength = rightindex - leftindex + 1; // 计算子序列的长度
                // 如果 subLength 的值不大于 result 则赋值给 result
                result = result < subLength ? result : subLength; 
                sum -= nums[leftindex++];
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};

这一道题主要是理解

  • 滑动窗口的概念
  • 当窗口内的所有元素和已经大于目标整数时, 如何变化左指针的值来继续遍历数组.
  • bool ? a : b; 表达式的理解, 如果bool内容为真, 则取a, 否则取b.
59.螺旋矩阵II

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

示例:

输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章讲解:代码随想录

视频讲解:一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili

思路:

首先确定输入的正整数n是奇数还是偶数, 然后由于是像一个循环一样的输出:

 我们从左到右,从上到下来判断输出的顺序和逻辑.

  • 首先定义一个二维数组来存取数据
  • 定义一个计数器count来写入数据
  • 第一次行输入最简单, 我们定义 i = 0 , j 从 0 遍历到 n - 1 写入count++;
  • 第一次列输入, j 刚好就在 n - 1 列上, 把 i 从 0 遍历到 n - 1 写入count++;
  • 第二次行输入, i 刚好就在 n - 1 行上, 把 j 从 n - 1 遍历到 0 写入 count++;
  • 第二次列输入....
  • 每四次输入是一个循环, 当我们下一次输入时, 最左边的边界增大1, 最右边的边界减小1, 因此我们可以设置一个 边界变化 offset, 每四次循环之后我们 offset += 1;
  • 每四次输入时, 我们发现最开始的输入一直是前四次输入的右下角位置, 因此我们可以定义两个变量 startx 和 starty, 我们一开始让 startx = starty = 0, 每四次遍历我们就让这两个变量加1, 表明下一次循环我们从上一次循环的右下角位置开始输入.
  • 最后如果输入的正整数n是奇数, 我们需要最后在中间位输入最后一个值(因为我们每次遍历不会停止在"->"这样一个位置) 定义一个 mid = n / 2, 最后填入 count(或n*n)即可

下面是代码部分: 

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> nums(n, vector<int>(n,0)); // 定义一个初始化为0的 n*n 的二维数组
        int startx = 0, starty = 0;
        int offset = 1;        // 边界改变量
        int count = 1;         // 填表计数器
        int mid = n / 2;       
        int loop = n / 2;      // 循环次数
        while(loop--){
            int j = starty;
            int i = startx;

            for(j = starty; j < n - offset; j++)        //'→'
                nums[startx][j] = count++;
            for(i = startx; i < n - offset; i++)        //'↓'
                nums[i][j] = count++;
            for( ; j > starty; j--)                     //'←'
                nums[i][j] = count++;
            for( ; i > startx; i--)                     //'↑'
                nums[i][j] = count++;

            startx ++;
            starty ++;           // 每次循环从右下角位置开始输入

            offset++;            // 每次循环边界向内变化1
        }

        if( n % 2 == 1 )                                 //奇数时填中间的元素
            nums[mid][mid] = count;

        return nums;
    }
};

总结:

今天花了4个小时来理解这三道题和博客理解, 收获颇多, 写完这篇博客的时候真的感觉这三道题已经掌握了许多, 再多理解一下双指针思路, 滑动窗口的思路和循环数组的思路. Day2感觉真不错啊.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值