代码随想录算法训练营第二天| 977.有序数组的平方,209.长度最小的子数组,59.螺旋矩阵II

977.有序数组的平方

Leetcode 977 题目链接

思路

题目要求处理的是按 非递减顺序 排序的整数数组,那么根据此排序特性,如果数组中存在正负数,那么该数组内元素平方的最大值,一定是在left和right这两个位置上。因为这个数组的绝对值变化呈先减后增趋势。那么我们就可以从两边开始,寻找最大值,一直探索到中间,直到所有的数字平方都记录在新的数组内。

下列两种代码都是运用了库函数,方便了算法过程。

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        return sorted(x**2 for x in nums)
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        for x in range(len(nums)):
            nums[x]*=nums[x]
        nums.sort()
        return nums

双指针法

由于我们确定了两边开始探索的方法,那么可以采用双指针法的双向指针来解决这道题。比较两边的数字的平方大小,大的一边,指针前进一格,并将数字记录在新数组内。简单来说,两个人相向而行,谁投的数大,谁就往前走一步,直到一个人超过另一个人(left>right,跳出循环)。

新数组需要我们重新创建一个,做不到在原有数组上既更新数字,又比较大小。新数组从大到小开始记录,且记入数字后,新数组内的存放指标往前移动(index-=1),等待下一个数。

代码

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        l=len(nums)
        result=[0]*l # 创造一个和nums等长的全是0的result,等会儿往里面填数
        left,right,index=0,l-1,l-1 # result从末尾开始往前填数字
        while left<=right: # 不把left=right也算作循环的条件的话,会导致left=right这个位置上的数字不被记入result
            if nums[left]**2>=nums[right]**2:
            # 当最左边数字平方大于等于最右边数字平方
                result[index]=nums[left]**2
                left+=1 # 左指针往右移动
                # 填入result最右边,left+1以排除已经记录的数字
            else:
            # 当最左边数字平方比最右边数字平方小
                result[index]=nums[right]**2
                right-=1 # 右指针往左移动
                # 把大的那个填入result最右边,right-1以排除已经记录的数字
            index-=1
            # if else判定完后,因为索引index的位置已经填入元素,index-1使存放结果指针往前移一位
        return result    

209.长度最小的子数组

Leetcode 209 原题链接

思路

如果是暴力解法的话,那就是两层循环,找出所有符合要求的子数组,最后得到长度最小的那一个。不过,因为暴力解法在leetcode运行会直接超时,所以我也不知道我自己的对不对…(bushi)

简便快捷的办法还是双指针法!

采用双指针法,right往前探索直到满足total>=target的条件,然后记录当前长度,固定right,left开始向前探索直到total<target,期间发现更小的区间则长度需要更新;最后right继续前进探索,直到到达边界,固定right,left持续前进,直到不再满足条件,同理,期间发现更小的区间则长度需要更新。

right作为主导的指针,每当其停止,left就要开始行动,直到right到达终止位置,left才会进行最后一次的探索。

滑动窗口

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。起始位置和终止位置可以理解为窗口的两边位置,而不是数组的起始和终止。起始位置是窗口开始向前移动的起点,而终止位置其实就是探索当前索引的数字是否能满足条件。

窗口就是 满足其和 ≥ target 的长度最小的连续子数组。

窗口的起始位置如何移动:如果当前窗口的值大于target了,窗口就要向前移动了(也就是该缩小了)。在这道题,我们的起始位置就是窗口的left。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。在这道题,我们的起始位置就是不断探索的right。

解题的关键在于 窗口的起始位置如何移动,如图所示:

在这里插入图片描述
相比于暴力解法,滑动窗口方法会在发现第一个满足条件的子数组时,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

代码

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        l=len(nums)
        left,right,total=0,0,0
        minlen=float('inf') # 设置无穷大是为了确保minlen在后续min()会被更新
        while right<l: # 当right=l,,会越界
            total+=nums[right] # 此时会把当前right的数加进去,无论left之后是否行动,right再+1
            while total>=target: # 右边界移动直到满足条件停止,这时候移动左边界判断当前区间能否更小
                minlen=min(minlen,right-left+1) # 比较当前连续子数组长度,最小就记录,不是最小就保持
                total-=nums[left] # 减去当前左区间的数字,下一步缩小区间
                left+=1 # 左边界右移一格,缩小区间
            right+=1 # 右边界右移一格进行下一轮循环判定
        return minlen if minlen != float('inf') else 0 
        # 跳出循环就返回minlen
        # 如果minlen是初始设置的无限大inf,则说明没有符合条件的子数组

59.螺旋矩阵II

Leetcode 59 原题链接

注意点

这类模拟行为不涉及到什么算法,就是单纯的模拟,但是掌握不好代码,还是容易被自己的循环绕晕,甚至想多了就不想动脑了。而对于循环,最需要注意的,无疑是一开始定义的区间规则,需要一直遵守下去

思路

螺旋矩阵的循环体现在转圈上。

一圈的过程是:
填充上行从左到右 --> 填充右列从上到下 --> 填充下行从右到左 --> 填充左列从下到上。在此基础上循环,由外向内填入数字。

下方两种,方法一致,但是上下左右区间设置不同,都写了以防以后弄乱了。

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        matrix=[[0]*n for _ in range(n)]
        l,r,t,b=0,n,0,n # 设置上下左右区间,此时r,b为考虑下方range设置为n
        num,end=1,n**2
        while num<=end: # 所赋的值来作为循环条件
             for i in range(l,r): # 填充上行,从左到右
                 matrix[t][i]=num # 固定在top行,列随着循环增加,
                 num+=1 # 记录数字逐渐增大
             t+=1 # 到达一圈的右上角,top+=1,进入到下一行
             for i in range(t,b): # 填充右列,从上到下
                 matrix[i][r-1]=num # 由于r=n且这一for循环要固定右列,r-1避免越界
                 num+=1
             r-=1 # 到达一圈的右下角,right-=1,往左移动一列
             for i in range(r-1,l-1,-1): # 填充下行,从右到左
             # r-1才开能取到,又因为range右为开区间,所以用取不到的l-1,-1表示逆向
                 matrix[b-1][i]=num # 由于b=n且这一for循环要固定下行,b-1避免越界
                 num+=1
             b-=1 # 到达一圈的左下角,bottom-=1,往上移动一行
             for i in range(b-1,t-1,-1): # 填充左列,从下到上
             # b-1才开能取到,又因为range右为开区间,所以用取不到的t-1,-1表示逆向
                 matrix[i][l]=num # 固定左列,填充数字
                 num+=1
             l+=1 # 到达一圈的左上角,left+=1,往右移动一行
             # 然后进入到下一圈内的起始点
        return matrix
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        matrix=[[0]*n for _ in range(n)]
        l,r,t,b=0,n-1,0,n-1
        num,end=1,n**2
        while num<=end:
            for i in range(l,r+1): # r+1确保取不到
                matrix[t][i]=num
                num+=1
            t+=1
            for i in range(t,b+1): # b+1确保取不到
                matrix[i][r]=num
                num+=1
            r-=1
            for i in range(r,l-1,-1):
                matrix[b][i]=num
                num+=1
            b-=1
            for i in range(b,t-1,-1):
                matrix[i][l]=num
                num+=1
            l+=1
        return matrix         

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值