代码随想录训练营打卡DAY 2 | 双指针+滑动窗口+螺旋矩阵

代码随想录训练营打卡DAY 2 | 双指针+滑动窗口+螺旋矩阵

双指针

977.有序数组的平方(leetcode链接)

题目描述

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

示例1:

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

题解

思路:看示例就很清楚了,可以直接每个都平方后再进行排序,暴力解法时间复杂度为O(nlogn)
笔者看到这里并不想直接暴力解,这个数组本身就是有序的;如果直接暴力解,就忽略了有序这个性质。

可以清楚地看到,要构造新数组,要么找到绝对值最小的数,从该点开始,双指针相背遍历;要么从数组两边开始,相向遍历。显然前者需要先找到绝对值最小的数,耗费时间更多,选取后者。
双指针法需要创建一个长度与nums相同的数组,笔者参考代码随想录文档后并没有找到原地执行的方法。

核心代码【C++】:

vector<int> sortedSquares(vector<int>& nums) {
    vector<int> newArray(nums.size());
    // 相向指针,左指针left,右指针right
    int left = 0, right = nums.size() - 1;
    int left_2,right_2;
    // 左右指针相向遍历,平方值大的填入新数组中,且新数组是从右到左填充
    for(int i = nums.size() - 1; i >= 0; i--){
        left_2 = nums[left] * nums[left];// 左值平方
        right_2 = nums[right] * nums[right];// 右值平方
        
        if(left_2 >= right_2){
            newArray[i] = left_2;
            left++;
        }
        else{
            newArray[i] = right_2;
            right--;
        }
    }
    return newArray;
}

时间复杂度:O(n)
空间复杂度:O(n)

滑动窗口

209.长度最小的子数组(leetcode链接)

题目描述

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例1:

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

示例2:

输入:target = 4, nums = [1,4,4]
输出:1

示例3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

题解

思路一:笔者第一时间连暴力求解都没有想到,脑袋里都是窗口滑来滑去;参考文档后发现可以使用暴力法求解,遂自行敲下了暴力解法。

核心代码【C++】:

int minSubArrayLen(int target, vector<int>& nums) {
    int i,j;
    int n = nums.size();
    int min = n;// 记录子串最小长度
    int sum = 0;
    for(i = 0; i < n; i++){
        sum += nums[i];
    }
    if(sum < target) return 0;// 没有符合要求的子串
    // 暴力求解,力扣超时不通过
    for(i = 0; i < n; i++){
        sum = 0;
        for(j = i; j < n; j++){
            sum += nums[j];
            if(sum >= target && (j-i+1) < min){
                min = j-i+1;
                break;// 这里其实只要找到符合条件的就可以跳出内层循环,笔者一开始没有写break;
            }
        }
    }
    return min;
}

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

思路二:滑动窗口
本题的滑动窗口长度是动态变化的,覆盖的位置也是动态变化的。这里就需要控制变量,变化窗口左端的时候右端不能变,反之亦然。窗口移动操作都是从左到右,过程如下:

  1. 先移动窗口右端,直到窗口内包含的数值>target(for循环实现)
  2. 接着移动窗口左端,直到窗口满足>target并且长度最小(while循环实现)
  3. 重复以上操作,记录下子串最小长度subLength

由以上过程可知,每个元素最多进入窗口一次,最多移出窗口一次,所以时间复杂度就是2*n,即 O ( n ) O(n) O(n)

核心代码【C++】:

int minSubArrayLen(int target, vector<int>& nums) {
    /*
    滑动窗口,本题是长度变化的窗口,每次只能变化窗口的一端不能同时变化
    1.先移动窗口右端,直到窗口内包含的数值>target(for循环实现)
    2.接着移动窗口左端,直到窗口满足>target并且长度最小(while循环实现)
    3.重复以上操作,记录下子串最小长度subLength
    */
    int result = INT32_MAX;
    int i = 0, j;// i为窗口左端,j为窗口右端
    int sum = 0;
    int subLength;
    for(j = 0; j < nums.size(); j++){// 移动窗口右端
        sum += nums[j];
        while(sum >= target){// 移动窗口左端
            sum -= nums[i++];
            subLength = j - i + 2;// 最后一次进循环后i本不该自增,但是上面++了,所以这里不是j - i + 1而是j - i + 2
            result = result > subLength ? subLength : result;
            // 其实可以将sum -= nums[i++];写在最后,此时subLength = j - i + 1
        }
    }
    return result == INT32_MAX ? 0 : result;
}

时间复杂度:O(n)
空间复杂度:O(1)

螺旋矩阵

59.螺旋矩阵(leetcode链接)

题目描述

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

在这里插入图片描述

示例1:

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

题解

思路:结合代码随想录文档提及的区间定义,笔者考虑到可以使用左闭右开的形式,直接这样说可能比较抽象。见下图:
在这里插入图片描述

  • 第一次循环区间为 j = [0,3)
  • 第二次循环区间为 i = [0,3)
  • 第三次循环区间为 j = [3,0),这里为倒序,即 j 取3,2,1
  • 第四次循环区间为 i = [3,0),这里为倒序,即 i 取3,2,1
  • 第五次循环区间为 j = [1,2)
  • 第六次循环区间为 i = [1,2)
  • 第七次循环区间为 j = [2,1),这里为倒序,即 j 取2
  • 第八次循环区间为 i = [2,1),这里为倒序,即 i 取2

既然循环区间大致知道如何了,接下来看看细节,每次循环结束后,由于i,j自增操作,指针会自动跳到下一个。同时设置一个自增变量k(图中1-16即k的值),每次指针移动一次便赋值给相应元素,k初值为1。
在这里插入图片描述

  • 解释:图中的 k 所在位置表示 k 的值,如该图表示 k 从1自增到4

  • 第一次循环结束后,指针刚好顺到下一循环区间的左端点,而k也自增到4,刚好可以填入到该位置。
    在这里插入图片描述

  • 4次循环结束(即遍历一圈)后,可以看到指针由于i–跑到了[0][0]位置,此时就需要将其移动到下一圈的开始,只要i++和j++即可。

  • 遍历完一圈后,k仍自增,需要填到[1][1]位置,经过上一步提及的i++和j++以后,指针和k值再次对应上。

  • 一圈结束后,下一圈的长度变小,所以设置offset来记录每个循环的次数。

这里需要注意一个问题,当n为奇数时,会陷入死循环,设置一个if语句打破死循环,最后还需要将k=n*n赋值到相应位置(详见代码)。

核心代码【C++】:

vector<vector<int>> generateMatrix(int n) {
    vector<vector<int>> matrix(n,vector<int>(n));
    int i = 0, j = 0;
    int k = 1;
    int offset = n - 1;
    while(k <= n*n){// n为偶数时正常运行,当n为奇数时,最后一个数k == n*n会陷入死循环
        if(k == n*n && k%2 == 1) break;//打破奇数导致的死循环
        while(k <= n*n && j < offset){
            matrix[i][j++] = k;
            k++;// 这里k++可以合并到上一行吗?
        }
        while(k <= n*n && i < offset){
            matrix[i++][j] = k;
            k++;
        }
        while(k <= n*n && j >= n - offset){
            matrix[i][j--] = k;
            k++;
        }
        while(k <= n*n && i >= n - offset){
            matrix[i--][j] = k;
            k++;
        }
        i++;j++;//将指针放到下一圈的起点
        offset--;//笔者第一想法是offset -= 2;但是其实有问题,因为下一圈开始的下标是上一圈开始下表i++和j++
    }
    // n为奇数时需要处理最后一个数字
    if(n%2 == 1) matrix[i][j] = k;
    return matrix;
}

模拟螺旋矩阵过程,需遍历完整个n*n矩阵,故时间复杂度为 O ( n 2 ) O(n^2) O(n2)

参考文档

  1. 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
  2. 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
  3. https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html
  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
代码随想录算法训练是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值