之前一直觉得leetcode刷的是对代码的熟练度,还有一些经典的数学算法。但做到今天发现更多的是一种思路,计算机专业的一些思考问题的想法。在都能用copilot写代码的今天,感觉这种想问题的方法是更重要的东西。
第一题:长度最小的子数组
题目描述如下:
看到这个题直接放弃,最直接的原因是不太会表示子集合。看了视频讲解,采用的是双指针法。视频的说法有点复杂,核心在于这个子数组的索引是连续的,比如[1,2,3]中,只认[1,2]和[2,3]为子数组,[1,3]就不算子数组了。这么一梳理思路就清晰了,写两个for循环嵌套。第一个循环用于所有的起始值,第二个循环用于截取窗口并判断窗口内的大小。令i从0开始取,j从0到n走len(nums)个,然后i从1开始取,j从1到n走len(nums)-1个,一直到i到n,此时j只有n可以走。可以看出来j的取值范围是(i,len(nums))。好的,那我就开始遍历,累加子数组的和为sum,创建一个空列表list1储存满足子数组和≥target的值时的长度。在每一次获得一次遍历j走完,要开启下一个i值的遍历时,将sum清零,存储新的一轮i值。最后,返回储存满足条件的子数组长度列表的最小值,如果列表为空的话,说明没有累加和≥target值的子数组,返回0。我的代码如下:
暴力解法被制裁了,通过了18个用例,有一个很bt的用例,target超大,炸内存了。无奈只能看看官方题解。题解里也有类似的暴力法,并且明确提出python写这种方法会被针对。
选择滑动窗口法,采用双指针left和right,都从0开始。先动right,当right小于l时,累加值cur_sum等于当前right的累加值,当累加值大于目标值时,取当前最小累加区间长度和右-左的数列片段中的最小值,作为新的累加区间长度,此时累加值要减去左边的值数,然后让左边的指针也动起来。这样可以进一步缩小区间,判断有没有更优的符合条件的最小长度。更新后的滑动窗口方法如下,我的理解是同样是双循环,第二个循环变成一个条件启动了,这样可以有效减免很多循环。采用双指针就是为了更好地不通过循环加来给出来这个条件。相应的,条件启动过后还要优化,因为本身条件启动会有一个考虑不全的问题,具体不全就不全在,前面的累加和算不上。
第二题:螺旋矩阵
题目描述如下:
...让我想起了selective scan,刚好最近在看mamba的论文,不过还是不一样的,那个是像响尾蛇一样来来回回。这次看论文也打算写点博客了,感觉是要比自己手写笔记要方便一些。
找规律吧,最中间的数[n-1,n-1]肯定是n^2,第一行是1到n,最右侧一列是n到2n-1,最下面是2n-1到3n-2。好的,别的看不出来了。
看了答案,就是直接遍历,像贪吃蛇一样往下写就行。从(0,0)开始,offset初始设置为1,最终循环到n//2+1时截止。why这么设置?offset这里的理解就是转一圈,贪吃蛇右转四次为一圈(事实证明右转右转右转右转不会回到原位hhh)。先从左到右走,这时候改变的只有列值(平行于x轴),要走的路程都等于列数-1=n-1,每前进一个单位下一个蛋,每下一个蛋贪吃蛇就长一些,nums[startx][i](行数不变,列数前进),蛋数+1。然后转弯,该向下走了,此时列数是不变的,要走的路程从开始的startx(第0列)往下走n个。依然下蛋,以此类推,直到转回来为止。每一个循环结束后并没有回到原点,起始点要各+1,因此startx+1,starty+1,要走的路程随即减少。
还有一个尴尬的问题,当n为奇数的时候,loop向下取整的,导致少转一圈,所以要填充一下中心值,中心值的索引是[mid,mid],填充值为现在出来的count。汇总代码如下:
这个题,可能关键就在于这个offset的设计能不能想到,通过offset控制行走的路程,导致每次循环走的次数不一样,完美按照题目给出的路径走出来。另外,用-1控制从右到左、从下到上的反方向行走。最后,在横着走的时候列索引不变,行索引依次+1。在竖着走的时候行索引不变,列索引依次+1。
明天再战,今天因为上班都没有看多少算法,倒是坚持了leetcode。其实还是应该把cv当成主业去做的,明天优先刷论文刷代码。