DAY2二刷-力扣977.有序数组的平方|力扣209.长度最小的子数组|59.螺旋矩阵II

力扣977.有序数组的平方

题目链接:力扣

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

这题用指针的契合点:数组中所有数都平方后,最大的数肯定是从数组的两端开始寻找。

 int* sortedSquares(int* nums, int numsSize, int* returnSize){
 //这个题目非常不好的地方就是returnsize这个函数参数,没什么用,而且题目描述的不清楚,让人很头疼,不看答案都不知道这个参数要干嘛。returnsize就是一个用来存放要返回的数组的大小的变量。
 int *result = (int*)malloc(sizeof(int) * numsSize);
 *returnSize = numsSize;//这行没啥用,单纯为了能过这题
 int k=numsSize-1;//新数组的元素序号(从大到小---题目要求)
 int i,j;
 for(i=0,j=numsSize-1;i<=j;)
 {
     if(nums[i]*nums[i]>nums[j]*nums[j])
     {
         result[k]=nums[i]*nums[i];
         i++;
         k--;
     }
     else
     {
         result[k]=nums[j]*nums[j];
         j--;
         k--;
     }
 }
 return result;
 }

小发现:二分查找中一次查找结束的条件是left>right;这道题里面一次检索结束的条件也是类似的,为i<=j;

力扣209.长度最小的子数组

本题的第一个难点在于对题目的理解,题目要求的是数组中长度最小的但是数组中所有值加起来大于target的子数组。

这题用滑动窗口的思想真的是第一次接触到!

 int minSubArrayLen(int target, int* nums, int numsSize){
 int result =numsSize+1;
 int left=0;//左指针
 int right=0;//右指针
 int sum=0;
 int lenght=0;//当前计算出的数组长度
 for(right =0;right<=numsSize-1;right++)
  {
     sum+=nums[right];
     while(sum>=target)
     {
         lenght=right-left+1;
         result=result>lenght?lenght:result;
         sum=sum-nums[left];
         left++;
     }
  }
  return result==numsSize+1?0:result;//易错,一定要注意判断结果
 }

这题如果不使用滑动窗口,按理是需要以数组中的每一个元素为新数组的起点,然后向后找到一个元素之和刚刚超过target的数组——》接着把分别以每个元素为起点的数组的长度进行比较,找到数组长度最小的数组,即为所求!

滑动窗口法的核心优点:但是如果按照上面最简单的思路的话,其实以每个元素为起点的时候,刚开始形成的那些长度只有1、2、3个元素的数组很大可能sum<target,所以这些情况下面还要去和target进行比较很浪费时间。滑动窗口法很巧妙使”以每个元素为起点的数组在一开始的sum值就能达到非常接近target的状态,这样就省去了很多没有意义的比较

滑动窗口是一种用于处理数组、字符串和链表的算法思想,它通常适用于需要计算连续、固定长度的元素序列的问题。具体来说,滑动窗口思想适用于以下类型的问题:

  1. 最大子数组/子串问题:寻找给定数组或字符串中元素之和或乘积最大的连续子区间/子串(本题)。

  2. 字符串匹配问题:在给定文本中查找是否存在与目标字符串匹配的子串--KMP(学过)。

  3. 数组/字符串分段问题:将给定序列划分成大小固定且不重叠的区间,并对每个区间执行某些操作。

  4. 滑动窗口代替哈希表:对于某些列举问题,如全排列、最小覆盖子串等问题,我们可能会使用哈希表形式的滑动窗口来解决。

59.螺旋矩阵II

题目链接:力扣

这道题的算法类型叫:模拟行为——模拟创建一个矩阵的行为。

思想:把一个大问题拆解成相似的小问题——》画螺旋矩阵的本质就是画一个个正方形。最后判断中间需不需要加上一个单独的值(非得要加上单独的只是因为loop=n除以2;就算把loop改成loop=n/2+1后,由于for循环里面都是j < startY + n - offset这种大于和小于的判断条件,而不是大于等于or小于等于,所以把loop改成n/2+1后也没有用)

难点:小问题之间的连贯性和转化(也就是解决了外圈矩阵的绘制后,如何把解决外圈的这一套算法放到解决内圈矩阵的绘制中)

 int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){
     *returnSize = n;//要返回的数组中有多少个数字
     *returnColumnSizes = (int*)malloc(sizeof(int) * n);
     //初始化返回数组为ans;ans是个二维数组
     int** ans = (int**)malloc(sizeof(int*) * n);
     int i;
     for(i = 0; i < n; i++) {
         ans[i] = (int*)malloc(sizeof(int) * n);
         (*returnColumnSizes)[i] = n;
     }
     //设置每次循环的起始位置
     int startX = 0;
     int startY = 0;
     //设置二维数组的中间值,若n为奇数。需要最后在中间填入数字
     int mid = n / 2;
     //循环圈数-->每转一圈到里面一圈时,矩阵的长和宽后会减2,所以圈数=n/2
     int loop = n / 2;
     //偏移数
     int offset = 1;
     //当前要添加的元素
     int count = 1;
 ​
     while(loop) {//只要loop不为0就继续循环
         int i = startX;
         int j = startY;
         //模拟上侧从左到右
         for(; j < startY + n - offset; j++) {
             ans[startX][j] = count++;
         }
         //模拟右侧从上到下
         for(; i < startX + n - offset; i++) {
             ans[i][j] = count++;
         }
         //模拟下侧从右到左
         for(; j > startY; j--) {
             ans[i][j] = count++;
         }
         //模拟左侧从下到上
         for(; i > startX; i--) {
             ans[i][j] = count++;
         }
         //偏移值每次加2
         offset+=2;
         //遍历起始位置的横纵坐标每次都+1(也就是起始位置往右下角移动了一格)——巧
         startX++;
         startY++;
         loop--;
     }
     //若n为奇数需要单独给矩阵中间赋值
     if(n%2)
         ans[mid][mid] = count;
 ​
     return ans;

这道题我觉得有个2个难点:

要确定出那么躲需要使用的变量,特别是loop、offset和二维数组的中间值mid

offset的推导公式——我觉得就是在不知道offset应该设置为多少的时候,将第二圈的startY 和n这两个变量的数值带入计算公式startY + n - offset中,然后看offset应该等于多少才能使这个循环正常。然后再把暂时确定下来的offset的值带入到第二圈别的边的计算和第三圈的计算中,看对不对,不对就再调整。

文章讲解:代码随想录

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

数组总结

一、数组

1.动态地申请数组空间:

 c语言:
 int*arr=(int*)malloc(sizeof(int)*length);
 c++:
 使用new关键字或者vector

2.二维数组的地址空间也是连续的

二、双指针算法

移除元素、有序数组的平方都用到了双指针的思想。而这些使用双指针的题目都是可以用暴力的方式解决的——》所以以后如果一个数组题目可以用暴力解决,那么就接下去考虑一下用双指针能不能解决。

双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

双指针法(快慢指针法)在数组和链表的操作中是非常常见的

三、滑动窗口

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

四、模拟行为

模拟类的题目在数组中很常见,不涉及到什么算法,就是单纯的模拟,十分考察大家对代码的掌控能力

在这道题目中,我们再一次介绍到了循环不变量原则,其实这也是写程序中的重要原则。

相信大家有遇到过这种情况: 感觉题目的边界调节超多,一波接着一波的判断,找边界,拆了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实真正解决题目的代码都是简洁的,或者有原则性的,大家可以在这道题目中体会到这一点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值