Leetcode刷题day2|数组二|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

news2024/11/15 8:07:43

一、有序数组的平方

题目链接

错误的尝试

一开始尝试双指针+原地完成(即空间复杂度为O(1))。

将所有的情况分成了全部大于等于0,全部小于等于0,有正有负三种情况,提出的对应方案是直接平方、平方并反转【用临时变量交换两端值,但是有三种情况老是同时解决只有一个、偶数个的情况、奇数个情况】、双指针【左边和右边绝对值比较,但是0和0挨着的情况总是需要特殊处理】。虽然这是一次错误的尝试,但是我通过debug、自己发现问题、解决部分问题的能力好像提升了hhhh。

总而言之,这次尝试失败的原因试图通过空间复杂度O(1)解决问题、问题拆分过于复杂。

思路

  1. 方法一:暴力求解,先把所有的数组元素平方放回原处,之后调用Arrays.sort进行排序。

  2. 方法二:临时数组+双指针方法。

    开辟一个临时数组,因为需要非递增序返回,但我们使用双指针找的绝对值/平方结果最大的值,所以我们为这个新数组定义索引,从初值为最后一个下标。

    双指针具体操作,左指针和右指针同时比较,找到最大的,如果选中左边的,左边左边加加,反之,右边减减,很显然这是一个循环过程,循环条件为左指针<=右指针即两者是否相遇

    最后,再将这个临时数组指向的地址赋给原数组。

注意

  1. 平方值最大的寻找,直接用平方比较即可,最好不要再调用Math.abs函数了= _ =
  2. 双指针循环中别忘了改变指针的值
  3. 新数组的下标开始从尾部开始

AC代码

暴力版本

class Solution {
    public int[] sortedSquares(int[] nums) {
        for(int i=0;i<nums.length;i++){
            nums[i]=nums[i]*nums[i];
        }
        Arrays.sort(nums);
        return nums;
    }
}

双指针方法

class Solution {
    public int[] sortedSquares(int[] nums) {
        int[] tmp=new int[nums.length];
        int k=nums.length-1;
        int left=0;
        int right=nums.length-1;
        while(left<=right){
            if(nums[left]*nums[left]>nums[right]*nums[right]){
                tmp[k--]=nums[left]*nums[left];
                left++;
            }else{
                tmp[k--]=nums[right]*nums[right];
                right--;
            }
        }
        nums=tmp;
        return nums;
    }
}

二、长度最小的子数组

题目链接

错误的尝试

一开始的思路是List<List>+get(i).size()+进行遍历寻找,但是当时脑子比较懵,想着应该是需要集合吧,应该是吧……

然后就又想,它只问我长度,也就是一个整形值,我为什么非要把结果存起来??这样不是绕弯路吗??然后想起来之前看过数组有个解题技巧叫做滑动窗口,然后又想滑动窗口怎么用?怎么滑?然后就没有然后了……

思路

核心解题思路:滑动窗口/双指针

这里我们采用滑动窗口解题,那么就必须要知道什么是滑动窗口,到底怎么使用?

滑动窗口介绍

  1. 首先什么是滑动窗口?

    滑动窗口就是一种通过不断的调节子序列起始位置终止位置,从而得出想要的子区间的数组求解办法,因为区间的滑动像一个窗户/窗口一样,所以我们又叫它滑动窗口。

    可以看到这里的关键就是起始位置和终止位置的求解。联想我们的双指针,相信不难得出,其实滑动窗口本质上就是双指针,只不过双指针关注的是数组中的两个下标,是两个点,而滑动窗口更关注的是通过对这两个下标区间的截取,是两个点确定的一个区间

  2. 那么滑动窗口问题应该怎么思考,怎么解决呢?

    从定义中我们可以看出,滑动窗口其实是由起始位置和终止位置唯一确定的。所以这个问题思考的方向可以聚焦到起始位置和终止位置的求解上。

    经过前辈们的总结,我们可以把滑动窗口问题分解成3部分:

    1)窗口内是什么?

    2)如何移动窗口的起始位置?

    3)如何移动窗口的结束位置?

看完滑动窗口的介绍,相信大家对这个已经有了一定的了解,下边我们围绕其中确定滑动窗口的三个步骤来思考这个问题。

1)窗口内是什么?

答:是满足同时满足是子数组子数组元素和>=target和长度最短的子数组。

2)如何移动窗口的起始位置?

定义下标i为起始位置,初始值为0,当前窗口内元素的和>=target,记录长度后,移动起始位置i.

3)如何移动窗口的结束位置?

定义遍历变量j,遍历数组,同时也代表当前窗口的结束位置。

定义变量sum,直至将数组元素值向里边加(起始对应的形象/具象概念也就是窗口不断扩大)。

当sum>=target时,我们能直接把用(当前位置-起始位置+1)作为结果吗?不行,反例如:[1,1,1,1,100],target=100,此时我们需要将起始位置向后移动。结果应该是1,而不是5。

那针对上述问题,我们应该怎么处理呢?用if判断可以吗?答不可以,需要用while循环,因为不确定起始位置需要向后移动几次。

经过上述讨论,我们已经可以确定本题的大概解题思路了。

1.定义起始位置i初值为0,定义循环变量j初值为0,范围是0~长度-1。【窗口相关变量】

2.定义sum,用于判断窗口的合法性,定义subList变量得到当前窗口的值,用于和之前的结果进行对比,取较小值,subList的确定需要循环

3.最终的ret初值需要是大于等于数组的长度,不然咱们设个-1,所有的都比咱们的大,就不能正确赋值了,别问,问就是已经栽过了。

注意

  1. ret初值的设置需要是大于等于数组的长度!!!!!
  2. 窗口的确定时,每次起始位置向后移动,sum也需要对应的减去对应起始位置的值
  3. 不要遗忘,最终窗口达到最大,仍然未满足条件,这个需要特殊处理

以上便是滑动窗口的解法,另外还有一种暴力求解问题的方法,就是双for循环,但是注意此时窗口的确定就不是while循环了,而是if语句。这里就不再说了,因为我这个暴力解法有点晕- -

AC代码

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int ret=nums.length;
        int subList=0;
        int sum=0;
        int tag=0;
        for(int i=0,j=0;j<nums.length;j++){
            sum+=nums[j];
            while(sum>=target){
                tag=1;
                subList=j-i+1;
                ret=ret>subList?subList:ret;
                sum-=nums[i++];
            }
        }
        if(tag==0){
            return 0;
        }
        return ret;
    }
}

三、螺旋矩阵

题目链接

错误的尝试

之前碰上过这道题,但是因为感觉下标的确定太麻烦了。没写过,即使当时看了题解。

昨天拿到这道题的时候就是想到了大概思路,既然是螺旋的,是圈的,那么我们我们就用个大循环控制圈数,里边写四个小循环控制每条边。但是说起来容易,做起来难,写的一直完全满足解决问题。

之后去看题解,发现,很多人说这道题比较简单,但是自己并不会,而且这个面试中考的也比较多,就突然惊醒了,说要自己动手去推一遍。

最终虽然没有写对,但是确实完成部分了,并且针对一些问题提出了解决方法。

比如说循环时要确定不变量,保证每次处理的情况,每次都是处理左闭右开的区间。

一开始写的是针对行不变还是列不变进行讨论。

再有就是对于奇数情况我们没有办法处理中心坐标的元素,此时我们就需要单独讨论

知道了到底旋转多少圈,不是n圈,而是n/2圈,但是当时又不想再定义临时变量来控制循环的结束

特别好的一点:处理区间、旋转圈数、奇数情况处理都是自己通过debug出来的!!!

思路

这道题,最终AC代码思路有两种

  1. 方法一:继承之前的i或者j的状态

    1)赋值时,所有的坐标格式都写成a[i][j]这种形式【可能会是a[startx][*]或者a[*][starty]这两种形式】

    2)循环的控制条件/圈数判断:startx<=n/2

    3)循环变量的改变:每次循环,startx++;starty++为什么同时加?因为前一圈结束后,我们每次需要处理的起始位置都应该是斜对角譬如b的位置,而不是x相邻的a位置

    对应的,结束位置也应该是斜对角的位置,而不是相邻的位置,我们这里定义了一个offset,用于表示不同圈数中每个子循环变量的边界。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sN16iwVq-1668669258587)(F:\typora插图\image-20221117145413210.png)]

    4)当奇数情况下,中心位置的元素在第n/2+1圈上,需要单独赋值

    1. 方法二:不继承,每次重新定义循环变量

      使用num <= tar而不是l < r || t < b作为迭代条件,是为了解决当n为奇数时,矩阵中心数字无法在迭代过程中被填充的问题。

      这里是看了力扣上卡佬的代码思路,感觉与自己最初的思路很像,就mark下来了。

注意

  1. 奇数情况,中心坐标需要特殊处理
  2. 保证每个子循环处理的情况都是一样的,这里应确保处理的都是左闭右开的区间【全集是每行/每列,大小是n】
  3. 注意每次不同圈数中怎么控制子循环边界
  4. 继承之前状态的代码中后两个子循环注意没有初始表达式。

AC代码

继承前边循环变量的写法

class Solution {
    public static int[][] generateMatrix(int n) {
        int[][] a=new int[n][n];
        int k=0;
        int startx=0,i=0;
        int starty=0,j=0;
        int offset=1;
        while(startx<n/2){
            for(j=starty;j<n-offset;j++){
                a[startx][j]=++k;
            }
            for(i=startx;i<n-offset;i++){
                a[i][j]=++k;
            }
            for(;j>starty;j--){
                a[i][j]=++k;
            }
            for(;i>startx;i--){
                a[i][j]=++k;
            }
            startx++;
            starty++;
            offset++;
        }
        if(n%2==1){
            a[n/2][n/2]=n*n;
        }
        return a;
    }
}

不继承前边循环变量的做法

class Solution {
    public int[][] generateMatrix(int n) {
        int l = 0, r = n - 1, t = 0, b = n - 1;
        int[][] mat = new int[n][n];
        int num = 1, tar = n * n;
        while(num <= tar){
            for(int i = l; i <= r; i++) mat[t][i] = num++; // left to right.
            t++;
            for(int i = t; i <= b; i++) mat[i][r] = num++; // top to bottom.
            r--;
            for(int i = r; i >= l; i--) mat[b][i] = num++; // right to left.
            b--;
            for(int i = b; i >= t; i--) mat[i][l] = num++; // bottom to top.
            l++;
        }
        return mat;
    }
}

四、数组做题思路总结

基本知识

内存中的存储

二分法的基本知识:模板、区间类型、循环不变量、区间类型确定的循环条件和更新操作

详见上一篇。

解题思路

  1. 双指针(关注某个点)

  2. 滑动窗口(关注区间)

另外,需要知道,数组并不能真正删除元素,只能通过覆盖的方式,同时注意i--

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/12554.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将爱心代码设为电脑屏保,俘获少女芳心,还能假装黑客,在酷炫的界面中保护隐私

本文介绍 Hacker Screen Saver 一款开源 Windows 屏保的使用。Hacker Screen Saver 是一款 .NET 设计的屏幕保护程序&#xff0c;可以显示 HTML 页面&#xff0c;你可以将黑客模拟器的网页&#xff0c;或者爱心代码网页设置为你的 Windows 电脑屏保。详细介绍了软件的使用和对应…

apritag 定位记录 C++ opencv 3.4.5

参考&#xff1a;2021-06-23 基于AprilTag的位姿估计&#xff0c;原理&#xff0c;完整代码&#xff08;相机坐标系、世界坐标系&#xff09; - 简书 Apriltag使用之二&#xff1a;方位估计(定位)_arczee的博客-CSDN博客_apriltag位姿估计 1.AprilTag概述 AprilTag是一种视觉…

Matlab:绘制日期时间

Matlab&#xff1a;绘制日期时间绘制日期时间数据指定坐标区范围指定刻度值指定刻度格式存储日期时间的坐标区属性导出和转换数据提示值绘制来自文件的日期时间数据此示例说明如何使用存储为 datetime 和 duration 数组的日期时间创建线图。datetime 数据类型表示时间点&#x…

Linux I/O 原理和 Zero-copy 技术全面分析

两万字长文从虚拟内存、I/O 缓冲区&#xff0c;用户态&内核态以及 I/O 模式等等知识点全面而又详尽地剖析 Linux 系统的 I/O 底层原理&#xff0c;分析了 Linux 传统的 I/O 模式的弊端&#xff0c;进而引入 Linux Zero-copy 零拷贝技术的介绍和原理解析&#xff0c;将零拷贝…

项目终于收尾了,第一次体验到专业项目管理软件的魅力

转眼到了年底&#xff0c;我跟进的项目也到了收尾阶段。之前陆陆续续给大家分享了入职新公司后&#xff0c;使用新引进的项目管理软件做项目的一些体会和心得&#xff0c;其中一些比较高效便捷的技巧和功能模块也引起了大家的兴趣。 最近刚好临近项目尾声&#xff0c;也给大家…

Maven的详解

在java中Maven就是一个包管理工具,在没有包管理工具时,我们要做一个java项目,需要第三方依赖包,将别人打包好的Jar包下载到本地,然后手动指定给项目.操作比较麻烦,比如版本控制,有的甚至还有其他包的依赖,属实是繁琐,技术是不断地迭代的,所以就出现了Maven,用了Maven之后,需要什…

安装nodejs的详细流程保姆级(踩了无数次坑)

node 简述: node的使用已经是前端选手基本的选择,其强大的功能甚至到了要和后端抢活干的地步,同时想要搭建个人的博客用node工具也是非常方便的,作为一名后端选上,刚开始准备下载node的时候是因为想要去搭建个人的博客,但是下载之后,使用npm install命令的时候一直报错,无奈找…

G1D14fraudgitpipenvdf操作APT论文RCE37-40服务器搭建

一、fraud 突然发现电脑上还没有python编译器&#xff0c;xswl&#xff0c;快装一下 &#xff08;一&#xff09;git操作 &#xff08;二&#xff09;git中分支的作用 &#xff08;三&#xff09;虚拟环境 1、pip install后的包一般放在哪里 lib/site-packages下 真的是欸&a…

LiveData

LiveData是一个抽象类&#xff0c;那么我们从简单的MutableLiiveData开始剖析&#xff0c;先看源码 源码太简洁了&#xff0c;就是继承LiveData&#xff0c;然后重写了父类的方法&#xff0c;并且没有多余的操作&#xff0c;都是直接使用父类方法里的逻辑&#xff0c;那我们就根…

安全防护的原则

电力行业 工控安全解决思路保障框架从电力行业对工控安全需求看&#xff0c;电力企业在主要是以合规性建设为主&#xff0c;在 2004 年原电监会 5 号令颁布开始&#xff0c;大部 分的电厂控制系统安全 建设已经按照 5 号令的要求进行了整改&#xff0c;形成“安全分区、网络专…

数电笔记总结(三)(逻辑门电路)

目录逻辑门基础逻辑门电路分立元件基本逻辑门电路TTL集成门电路&#xff08;与非门&#xff09;两种特殊门&#xff08;重点&#xff09;1.集电极开路门&#xff08;OC门&#xff09;2.三态门电路逻辑门基础 逻辑门电路 门电路&#xff1a;具有控制信号通过或不通过能力的电路…

某某桥的检测和加固设计

目录 某某大桥桥梁检测及加固设计报告 1 0 总论 2 0.1 检测目的 2 0.2 桥梁结构混凝土强度检测[1] 2 0.3 结构综合评定指标 4 0.4桥梁承载能力[3] 4 0.5 桥梁结构荷载试验 6 0.6 主要结果与结论 8 1某某大桥简介 11 1.1某某大桥简介 11 1.2 检测仪器与设备 15 2 外观检查与检测…

【跟学C++】C++STL标准模板库——算法整理(上)(Study18)

文章目录1、STL简介2、STL算法分类及常用函数2.1、非变序算法2.1.1 计数算法(2个)2.1.2 搜索算法(7个)2.1.3 比较算法(2个)3、总结 【说明】 大家好&#xff0c;本专栏主要是跟学C内容&#xff0c;自己学习了这位博主【 AI菌】的【C21天养成计划】&#xff0c;讲的十分清晰&am…

每个程序员都要知道的一个网站

在日常开发过程中&#xff0c;你是不是经常回到搜索引擎&#xff0c;搜索某个功能的实现方式&#xff0c;比如&#xff1a;Javascript 数组排序、正则表达式等等。 今天给大家推荐的这个网站&#xff0c;就可以满足大家的需求&#xff0c;它叫&#xff1a;30secondsofcode&…

抽象类与接口

目录 1. 抽象类 1.1 抽象类概念 1. 2&#x1f414;抽象类特性 1.3 抽象类的作用 2. 接口 2.1 接口是什么 2.2 语法规则 2.3 方法的使用 2.4 接口特性 2.5 实现接口 VS 继承类 2.6 抽象类 VS 接口&#xff08;总结&#xff09; 2.6 接口间的继承 &#x1f413; 随着…

漫画脸头像怎么制作?这几种方法可以帮到你

你们会经常更换头像吗&#xff1f;我身边就有一些朋友会这样做&#xff0c;看到喜欢的头像就换&#xff0c;而且他基本上都是找那些漫画脸来当头像。那如果我们把自己的人像制作成漫画脸&#xff0c;就不容易跟别人撞头像了&#xff0c;还显得有个性。 估计有很多小伙伴不知道漫…

NXP BootLoader源码分析并改写SD卡启动

1 官方资料 NXP官方提供了MCUBoot SDK&#xff1a;NXP_Kinetis_Bootloader_2.0.0 package&#xff0c;里面包含了各种型号芯片的的BootLoader。 BootLoader参考手册&#xff1a;doc/Kinetis Bootloader v2.0.0 Reference Manual.pdf上位机程序参考手册&#xff1a;Kinetis Fl…

疯狂小杨哥被王海打假

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 知名打假人王海 发布视频&#xff0c;说疯狂小杨哥三只羊直播间售卖的金正破壁机和绞肉机虚标功率。破壁机标注功率为300W&#xff0c;实际为105W&#xff0c;绞肉机标注功率300W&#xff0c;实际功…

[附源码]java毕业设计咖啡销售管理系统-

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

总线仲裁的方式

总线仲裁的基本概念 同一时刻只能有一个设备控制总线传输操作&#xff0c;可以有一个或多个设备从总线接收数据。 将总线上所连接的各类设备按其对总线有无控制功能分为&#xff1a; 主设备&#xff1a;获得总线控制权的设备。 从设备&#xff1a;被主设备访问的设备&#xff0…