算法记录Day2 | 977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II

977.有序数组的平方

文章链接:977.有序数组的平方 - 力扣(LeetCode)

这是我写的程序:

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
		
        # 数组为长度为1的情况
        if len(nums) == 1: 
            nums[0] = nums[0]**2
            return nums

        # 将原数组平方
        for i in range(len(nums)):
            nums[i] = nums[i]**2

        # 找到最小值的坐标
        start = 0
        while nums[start]>=nums[start+1]:
            start += 1
            if start == len(nums)-1: break

        left = 0
        rightSmall, rightBig = start, start+1

        right = rightSmall
        numsNew = [0]*len(nums)   # 定义新数组
        if rightBig == len(nums): 
            right = rightBig
            for i in range(len(numsNew)):
                numsNew[i] = nums[len(nums)-1-i]

        while right >= 0 and right < len(nums) :
            if nums[rightSmall] <= nums[rightBig]:
                numsNew[left] = nums[rightSmall]
                rightSmall -= 1
                right = rightSmall
            elif nums[rightSmall] > nums[rightBig]:
                numsNew[left] = nums[rightBig]
                rightBig += 1
                right = rightBig
            left += 1

        # 跳出循环,即代表指针right超出了数组下标的范围,分情况特殊处理(某一方超出不代表另一边也到达了边界)
        if rightSmall < 0: 
            while left<len(nums):
              numsNew[left] = nums[rightBig]
              left += 1
              rightBig += 1
        elif rightBig > len(nums)-1: 
            while left<len(nums):
                numsNew[left] = nums[rightSmall]
                left += 1
                rightSmall -= 1

        return numsNew

运用了双指针的思想,但我最初的思路是从中间的最小值往两边遍历,比较左右值的大小,小者则进入新数组,程序调试了很久,加了许多判定条件最终才全部通过了。

代码随想录中的程序(双指针法):

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 双指针法
        for i in range(len(nums)):
            nums[i] = nums[i]**2

        slow = len(nums)-1
        fastSmall, fastBig = 0, len(nums)-1
        numsNew = [0]*len(nums)

        while fastSmall <= fastBig:
            if nums[fastSmall] > nums[fastBig]:
                numsNew[slow] = nums[fastSmall]
                fastSmall += 1
            else:
                numsNew[slow] = nums[fastBig]
                fastBig -= 1
            slow -= 1
        return numsNew

这是参考了代码随想录的程序,他的思路是从两边往中间遍历,最终跳出循环的条件为:fastSmall > fastBig,即“左边”的值不再小于等于“右边”的值,这样代码就简洁很多了,果然写算法题还是思路最重要!

209.长度最小的子数组

题目链接:209.长度最小的子数组 - 力扣(LeetCode)

这是我写的程序(暴力解法):

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 暴力算法
        for i in range(1, len(nums)+1):
            for j in range(len(nums)+1-i):
                sum = 0
                for k in range(i):
                    sum += nums[j+k]
                if sum >= target: return i
        return 0

两层遍历,第三个for是将窗口内的值累加,程序由于运行超时而没有全部通过。

代码随想录中的程序(滑动窗口):

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
	  # 滑动窗口
        i= 0   # 滑动窗口起始位置
        result = float("inf")   # 定义一个无限大的数
        sum = 0   # 滑动窗口数值之和
        for j in range(len(nums)):
            sum += nums[j]
            while sum >= target:
                length = j-i+1
                result = min(result, length)
                sum -= nums[i]
                i += 1
        return 0 if result == float("inf") else result

滑动窗口的思想很巧妙。(以下引用自算法训练营中的总结)

  • 滑动窗口:本质是满足了单调性,即左右指针只会往一个方向走且不会回头。收缩的本质即去掉不再需要的元素。也就是做题我们可以先固定移动右指针,判断条件是否可以收缩左指针算范围。

  • 滑动窗口的原理是右边先开始走,然后直到窗口内值的总和大于target,此时就开始缩圈,缩圈是为了找到最小值,只要此时总和还大于target,我就一直缩小,缩小到小于target为止在这过程中不断更新最小的长度值,然后右边继续走,如此反复,直到右边碰到边界。这样就保证了可以考虑到最小的情况。

59.螺旋矩阵II

题目链接:59.螺旋矩阵II - 力扣(LeetCode)

这道题感觉有点难上手,直接看的视频,再参考代码随想录写的程序。

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        nums = [[0] * n for _ in range(n)]   # 生成一个二维数组
        startx, starty = 0, 0                # 起始位置x和y
        loop = n // 2                        # 圈数
        count = 1                            # 计数

        for offset in range(1, loop+1):   # offset表示偏移量,每转一圈偏移量得增加1
            for j in range(starty, n-offset):
                nums[startx][j] = count
                count += 1
            for i in range(startx, n-offset):
                nums[i][n-offset] = count
                count += 1
            for j in range(n-offset, starty, -1):
                nums[n-offset][j] = count
                count += 1
            for i in range(n-offset, startx, -1):
                nums[i][starty] = count
                count += 1
            startx += 1
            starty += 1
            offset += 1
        
        mid = n // 2   # 若n为奇数,则需求最中心点(mid)
        if (n%2) != 0:
            nums[mid][mid] = count

        return nums

需注意的点:

  • 此题要坚持循环不变量原则,区间的定义就是不变量,上面程序采用的是左闭右开的区间定义;
  • nums = [[0] * n for _ in range(n)] 为生成一个二维数组,_ 只是一个占位符,只在乎遍历次数range(n),即遍历n次;
  • range(大的数, 小的数),这样写是错误的,应为 range(大的数, 小的数, -1)。
    range(start, stop[, step]) :
    start:计数从 start 开始,默认是从 0 开始,例如,range(6) 等价于 range(0, 6);
    stop:计数到 stop 结束,但不包括 stop,例如,range(0, 6) 是 [0, 1, 2, 3, 4, 5] (不包括6);
    step:步长,默认为1,递减应为负数,例如:range(0, 6) 等价于 range(0, 6, 1),range(6, 0, -1)是 [6, 5, 4, 3, 2, 1] (不包括0)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值